remote-components 0.2.2 → 0.3.1

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 (100) hide show
  1. package/dist/config/nextjs.cjs +2 -4
  2. package/dist/config/nextjs.cjs.map +1 -1
  3. package/dist/config/nextjs.d.ts +1 -1
  4. package/dist/config/nextjs.js +2 -4
  5. package/dist/config/nextjs.js.map +1 -1
  6. package/dist/host/html.cjs +128 -112
  7. package/dist/host/html.cjs.map +1 -1
  8. package/dist/host/html.js +128 -115
  9. package/dist/host/html.js.map +1 -1
  10. package/dist/host/nextjs/app/client-only.cjs +233 -259
  11. package/dist/host/nextjs/app/client-only.cjs.map +1 -1
  12. package/dist/host/nextjs/app/client-only.js +234 -260
  13. package/dist/host/nextjs/app/client-only.js.map +1 -1
  14. package/dist/host/nextjs/app.cjs +5 -6
  15. package/dist/host/nextjs/app.cjs.map +1 -1
  16. package/dist/host/nextjs/app.js +5 -6
  17. package/dist/host/nextjs/app.js.map +1 -1
  18. package/dist/host/nextjs/pages.cjs +7 -19
  19. package/dist/host/nextjs/pages.cjs.map +1 -1
  20. package/dist/host/nextjs/pages.js +11 -20
  21. package/dist/host/nextjs/pages.js.map +1 -1
  22. package/dist/host/react.cjs +101 -93
  23. package/dist/host/react.cjs.map +1 -1
  24. package/dist/host/react.js +101 -93
  25. package/dist/host/react.js.map +1 -1
  26. package/dist/internal/host/nextjs/app-client.cjs +3 -8
  27. package/dist/internal/host/nextjs/app-client.cjs.map +1 -1
  28. package/dist/internal/host/nextjs/app-client.js +4 -9
  29. package/dist/internal/host/nextjs/app-client.js.map +1 -1
  30. package/dist/internal/host/nextjs/dom-flight.cjs +16 -7
  31. package/dist/internal/host/nextjs/dom-flight.cjs.map +1 -1
  32. package/dist/internal/host/nextjs/dom-flight.d.ts +2 -2
  33. package/dist/internal/host/nextjs/dom-flight.js +16 -7
  34. package/dist/internal/host/nextjs/dom-flight.js.map +1 -1
  35. package/dist/internal/host/nextjs/image-shared.cjs +25 -15
  36. package/dist/internal/host/nextjs/image-shared.cjs.map +1 -1
  37. package/dist/internal/host/nextjs/image-shared.d.ts +19 -6
  38. package/dist/internal/host/nextjs/image-shared.js +24 -14
  39. package/dist/internal/host/nextjs/image-shared.js.map +1 -1
  40. package/dist/internal/host/react/hooks/use-resolve-client-url.cjs +1 -5
  41. package/dist/internal/host/react/hooks/use-resolve-client-url.cjs.map +1 -1
  42. package/dist/internal/host/react/hooks/use-resolve-client-url.d.ts +1 -4
  43. package/dist/internal/host/react/hooks/use-resolve-client-url.js +1 -5
  44. package/dist/internal/host/react/hooks/use-resolve-client-url.js.map +1 -1
  45. package/dist/internal/host/server/fetch-remote-component.cjs +164 -149
  46. package/dist/internal/host/server/fetch-remote-component.cjs.map +1 -1
  47. package/dist/internal/host/server/fetch-remote-component.js +166 -149
  48. package/dist/internal/host/server/fetch-remote-component.js.map +1 -1
  49. package/dist/internal/host/shared/polyfill.cjs +10 -65
  50. package/dist/internal/host/shared/polyfill.cjs.map +1 -1
  51. package/dist/internal/host/shared/polyfill.d.ts +1 -3
  52. package/dist/internal/host/shared/polyfill.js +9 -63
  53. package/dist/internal/host/shared/polyfill.js.map +1 -1
  54. package/dist/internal/host/shared/remote-image-loader.cjs +53 -0
  55. package/dist/internal/host/shared/remote-image-loader.cjs.map +1 -0
  56. package/dist/internal/host/shared/remote-image-loader.d.ts +30 -0
  57. package/dist/internal/host/shared/remote-image-loader.js +29 -0
  58. package/dist/internal/host/shared/remote-image-loader.js.map +1 -0
  59. package/dist/internal/runtime/constants.cjs +6 -6
  60. package/dist/internal/runtime/constants.cjs.map +1 -1
  61. package/dist/internal/runtime/constants.d.ts +3 -3
  62. package/dist/internal/runtime/constants.js +4 -4
  63. package/dist/internal/runtime/constants.js.map +1 -1
  64. package/dist/internal/runtime/html/parse-remote-html.cjs +11 -15
  65. package/dist/internal/runtime/html/parse-remote-html.cjs.map +1 -1
  66. package/dist/internal/runtime/html/parse-remote-html.d.ts +2 -12
  67. package/dist/internal/runtime/html/parse-remote-html.js +17 -15
  68. package/dist/internal/runtime/html/parse-remote-html.js.map +1 -1
  69. package/dist/internal/runtime/loaders/script-loader.cjs +2 -2
  70. package/dist/internal/runtime/loaders/script-loader.cjs.map +1 -1
  71. package/dist/internal/runtime/loaders/script-loader.js +1 -1
  72. package/dist/internal/runtime/loaders/script-loader.js.map +1 -1
  73. package/dist/internal/runtime/metadata.cjs +42 -0
  74. package/dist/internal/runtime/metadata.cjs.map +1 -1
  75. package/dist/internal/runtime/metadata.d.ts +21 -1
  76. package/dist/internal/runtime/metadata.js +38 -0
  77. package/dist/internal/runtime/metadata.js.map +1 -1
  78. package/dist/internal/runtime/patterns.cjs +38 -0
  79. package/dist/internal/runtime/patterns.cjs.map +1 -0
  80. package/dist/internal/runtime/patterns.d.ts +5 -0
  81. package/dist/internal/runtime/patterns.js +12 -0
  82. package/dist/internal/runtime/patterns.js.map +1 -0
  83. package/dist/internal/runtime/turbopack/chunk-loader.cjs +4 -3
  84. package/dist/internal/runtime/turbopack/chunk-loader.cjs.map +1 -1
  85. package/dist/internal/runtime/turbopack/chunk-loader.js +1 -1
  86. package/dist/internal/runtime/turbopack/chunk-loader.js.map +1 -1
  87. package/dist/internal/runtime/turbopack/webpack-runtime.cjs +11 -2
  88. package/dist/internal/runtime/turbopack/webpack-runtime.cjs.map +1 -1
  89. package/dist/internal/runtime/turbopack/webpack-runtime.js +10 -2
  90. package/dist/internal/runtime/turbopack/webpack-runtime.js.map +1 -1
  91. package/dist/remote/nextjs/app.cjs +2 -1
  92. package/dist/remote/nextjs/app.cjs.map +1 -1
  93. package/dist/remote/nextjs/app.js +2 -1
  94. package/dist/remote/nextjs/app.js.map +1 -1
  95. package/package.json +1 -1
  96. package/dist/internal/host/nextjs/image-impl.cjs +0 -64
  97. package/dist/internal/host/nextjs/image-impl.cjs.map +0 -1
  98. package/dist/internal/host/nextjs/image-impl.d.ts +0 -10
  99. package/dist/internal/host/nextjs/image-impl.js +0 -40
  100. package/dist/internal/host/nextjs/image-impl.js.map +0 -1
@@ -211,58 +211,35 @@ function getClientOrServerUrl(src, serverFallback) {
211
211
  return typeof src === "string" ? new URL(src, fallback) : src;
212
212
  }
213
213
 
214
- // src/host/shared/polyfill.tsx
215
- import { jsx } from "react/jsx-runtime";
216
- function applyBundleUrlToSrc(bundle, src) {
214
+ // src/host/shared/remote-image-loader.ts
215
+ function getRemoteBundleOrigin(bundle) {
217
216
  const self = globalThis;
218
- if (self.__remote_bundle_url__?.[bundle]?.origin === location.origin) {
219
- return src;
220
- }
221
- const { assetPrefix, path } = /^(?<assetPrefix>.*?)\/_next\/(?<path>.*)/.exec(src)?.groups ?? {};
222
- if (!path) {
223
- return new URL(src, self.__remote_bundle_url__?.[bundle]?.origin).href;
224
- }
225
- return `${self.__remote_bundle_url__?.[bundle]?.origin ?? ""}${assetPrefix}/_next/${path}`;
226
- }
227
- function applyBundleUrlToImagePropsSrc(bundle, src) {
228
- if (typeof src === "string") {
229
- return applyBundleUrlToSrc(bundle, src);
230
- }
231
- const propSrc = src;
232
- return applyBundleUrlToSrc(bundle, propSrc.src);
233
- }
234
- var imageImpl = (bundle, resolveClientUrl) => function RemoteImage({
235
- fill: _fill,
236
- loader: _loader,
237
- quality: _quality,
238
- priority: _priority,
239
- loading: _loading,
240
- placeholder: _placeholder,
241
- blurDataURL: _blurDataURL,
242
- unoptimized: _unoptimized,
243
- overrideSrc: _overrideSrc,
244
- src,
245
- ...props
246
- }) {
247
- const newSrc = applyBundleUrlToImagePropsSrc(
248
- bundle,
249
- typeof src === "string" ? src : src.src
250
- );
251
- const proxiedSrc = resolveClientUrl?.(newSrc) ?? newSrc;
252
- return (
253
- // eslint-disable-next-line @next/next/no-img-element, jsx-a11y/alt-text
254
- /* @__PURE__ */ jsx(
255
- "img",
256
- {
257
- decoding: "async",
258
- style: { color: "transparent" },
259
- ...props,
260
- src: proxiedSrc,
261
- suppressHydrationWarning: true
262
- }
263
- )
217
+ return self.__remote_bundle_url__?.[bundle]?.origin ?? "";
218
+ }
219
+ function createRemoteImageLoader(bundle, resolveClientUrl) {
220
+ const loader = Object.assign(
221
+ ({
222
+ config,
223
+ src,
224
+ width,
225
+ quality
226
+ }) => {
227
+ const q = quality ?? 75;
228
+ const remoteOrigin = getRemoteBundleOrigin(bundle);
229
+ const isCrossOrigin = remoteOrigin && remoteOrigin !== location.origin;
230
+ const basePath = isCrossOrigin ? `${remoteOrigin}${config.path ?? "/_next/image"}` : config.path ?? `${remoteOrigin}/_next/image`;
231
+ const url = `${basePath}?url=${encodeURIComponent(src)}&w=${width}&q=${q}`;
232
+ return resolveClientUrl?.(url) ?? url;
233
+ },
234
+ // Signals to getImgProps that this is a default loader (not a user-defined
235
+ // one), enabling srcSet generation with device/image sizes from the config.
236
+ { __next_img_default: true }
264
237
  );
265
- };
238
+ return loader;
239
+ }
240
+
241
+ // src/host/shared/polyfill.tsx
242
+ import { jsx } from "react/jsx-runtime";
266
243
  function sharedPolyfills(shared, resolveClientUrl) {
267
244
  const self = globalThis;
268
245
  const polyfill = {
@@ -353,17 +330,13 @@ function sharedPolyfills(shared, resolveClientUrl) {
353
330
  },
354
331
  __esModule: true
355
332
  })),
356
- "next/dist/client/image-component": self.__remote_component_host_shared_modules__?.["next/image"] ?? shared?.["next/image"] ?? ((bundle) => Promise.resolve({
357
- Image: imageImpl(bundle, resolveClientUrl),
358
- __esModule: true
359
- })),
360
- "next/image": self.__remote_component_host_shared_modules__?.["next/image"] ?? shared?.["next/image"] ?? ((bundle) => Promise.resolve({
361
- default: imageImpl(bundle, resolveClientUrl),
362
- getImageProps: (_imgProps) => {
363
- throw new Error(
364
- "Next.js getImageProps() is not implemented in remote components"
365
- );
366
- },
333
+ // Instead of replacing next/image entirely, we let the real Next.js Image
334
+ // component load from the remote bundle and only replace its default loader.
335
+ // This gives us full next/image fidelity (fill, priority, srcSet, blur
336
+ // placeholders, error handling) while routing image optimization through the
337
+ // remote app's /_next/image endpoint.
338
+ "next/dist/shared/lib/image-loader": self.__remote_component_host_shared_modules__?.["next/dist/shared/lib/image-loader"] ?? shared?.["next/dist/shared/lib/image-loader"] ?? ((bundle) => Promise.resolve({
339
+ default: createRemoteImageLoader(bundle, resolveClientUrl),
367
340
  __esModule: true
368
341
  })),
369
342
  "next/dist/client/script": self.__remote_component_host_shared_modules__?.["next/script"] ?? shared?.["next/script"] ?? (() => Promise.resolve({
@@ -403,7 +376,7 @@ function sharedPolyfills(shared, resolveClientUrl) {
403
376
  polyfill["next/navigation"] = polyfill["next/dist/client/components/navigation"];
404
377
  polyfill["next/link"] = polyfill["next/dist/client/app-dir/link"];
405
378
  polyfill["next/form"] = polyfill["next/dist/client/app-dir/form"];
406
- polyfill["next/dist/api/image"] = polyfill["next/dist/client/image-component"];
379
+ polyfill["next/dist/esm/shared/lib/image-loader"] = polyfill["next/dist/shared/lib/image-loader"];
407
380
  polyfill["next/script"] = polyfill["next/dist/client/script"];
408
381
  return polyfill;
409
382
  }
@@ -447,12 +420,12 @@ var attrToProp = {
447
420
  };
448
421
 
449
422
  // src/runtime/constants.ts
423
+ var DEFAULT_BUNDLE_NAME = "__vercel_remote_bundle";
424
+ var DEFAULT_COMPONENT_NAME = "__vercel_remote_component";
450
425
  var DEFAULT_ROUTE = "/";
451
426
  var RUNTIME_WEBPACK = "webpack";
452
427
  var RUNTIME_TURBOPACK = "turbopack";
453
428
  var RUNTIME_SCRIPT = "script";
454
- var REMOTE_COMPONENT_REGEX = /(?<prefix>.*?)\[(?<bundle>[^\]]+)\](?:%20| )(?<id>.+)/;
455
- var NEXT_BUNDLE_PATH_RE = /\/_next\/\[.+\](?:%20| )/;
456
429
  function getBundleKey(bundle) {
457
430
  return escapeString(bundle);
458
431
  }
@@ -519,6 +492,38 @@ function applyOriginToNodes(doc, url, resolveClientUrl) {
519
492
  }
520
493
  }
521
494
 
495
+ // src/runtime/metadata.ts
496
+ var VALID_RUNTIMES = /* @__PURE__ */ new Set(["webpack", "turbopack", "script"]);
497
+ var VALID_TYPES = /* @__PURE__ */ new Set([
498
+ "nextjs",
499
+ "remote-component",
500
+ "unknown"
501
+ ]);
502
+ function isRuntime(value) {
503
+ return VALID_RUNTIMES.has(value);
504
+ }
505
+ function isComponentType(value) {
506
+ return VALID_TYPES.has(value);
507
+ }
508
+ function toRuntime(value) {
509
+ return value && isRuntime(value) ? value : "webpack";
510
+ }
511
+ function toComponentType(value) {
512
+ return value && isComponentType(value) ? value : "unknown";
513
+ }
514
+ function buildMetadata(attrs, url) {
515
+ const id = attrs.id || DEFAULT_COMPONENT_NAME;
516
+ const bundle = attrs.bundle || process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION || DEFAULT_BUNDLE_NAME;
517
+ return {
518
+ name: attrs.name || id.replace(/_ssr$/, ""),
519
+ bundle,
520
+ route: attrs.route || url.pathname || DEFAULT_ROUTE,
521
+ runtime: toRuntime(attrs.runtime),
522
+ id,
523
+ type: toComponentType(attrs.type)
524
+ };
525
+ }
526
+
522
527
  // src/runtime/html/parse-remote-html.ts
523
528
  function validateSingleComponent(doc, name, url) {
524
529
  if (doc.querySelectorAll("div[data-bundle][data-route]").length > 1 && !doc.querySelector(`div[data-bundle][data-route][id^="${name}"]`) || doc.querySelectorAll("remote-component:not([src])").length > 1 && !doc.querySelector(`remote-component[name="${name}"]`)) {
@@ -538,14 +543,6 @@ function resolveComponentName(component, nextData, fallbackName) {
538
543
  const name = component?.getAttribute("id")?.replace(/_ssr$/, "") || isRemoteComponent && component?.getAttribute("name") || (nextData ? "__next" : fallbackName);
539
544
  return { name, isRemoteComponent };
540
545
  }
541
- function extractComponentMetadata(component, nextData, name, url) {
542
- return {
543
- name,
544
- bundle: component?.getAttribute("data-bundle") || nextData?.props.__REMOTE_COMPONENT__?.bundle || "default",
545
- route: component?.getAttribute("data-route") ?? nextData?.page ?? (url.pathname || DEFAULT_ROUTE),
546
- runtime: component?.getAttribute("data-runtime") ?? (nextData?.props.__REMOTE_COMPONENT__?.runtime || RUNTIME_SCRIPT)
547
- };
548
- }
549
546
  function extractRemoteShared(doc, name, nextData) {
550
547
  const remoteSharedEl = doc.querySelector(
551
548
  `#${name}_shared[data-remote-components-shared]`
@@ -557,7 +554,7 @@ function extractRemoteShared(doc, name, nextData) {
557
554
  function validateComponentFound(component, rsc, nextData, isRemoteComponent, url, name) {
558
555
  if (!component || !(rsc || nextData || isRemoteComponent)) {
559
556
  throw new RemoteComponentsError(
560
- `Remote Component not found on ${url}.${name !== "__vercel_remote_component" ? ` The name for the <RemoteComponent> is "${name}". Check <RemoteComponent> usage.` : ""} Did you forget to wrap the content in <RemoteComponent>?`
557
+ `Remote Component not found on ${url}.${name !== DEFAULT_COMPONENT_NAME ? ` The name for the <RemoteComponent> is "${name}". Check <RemoteComponent> usage.` : ""} Did you forget to wrap the content in <RemoteComponent>?`
561
558
  );
562
559
  }
563
560
  }
@@ -583,10 +580,15 @@ function parseRemoteComponentDocument(doc, name, url) {
583
580
  name
584
581
  );
585
582
  const rsc = doc.querySelector(`#${resolvedName}_rsc`);
586
- const metadata = extractComponentMetadata(
587
- component,
588
- nextData,
589
- resolvedName,
583
+ const metadata = buildMetadata(
584
+ {
585
+ name: resolvedName,
586
+ bundle: component?.getAttribute("data-bundle") || nextData?.props.__REMOTE_COMPONENT__?.bundle,
587
+ route: component?.getAttribute("data-route") ?? nextData?.page,
588
+ runtime: component?.getAttribute("data-runtime") ?? nextData?.props.__REMOTE_COMPONENT__?.runtime ?? RUNTIME_SCRIPT,
589
+ id: component?.getAttribute("id"),
590
+ type: component?.getAttribute("data-type")
591
+ },
590
592
  url
591
593
  );
592
594
  const remoteShared = extractRemoteShared(doc, resolvedName, nextData);
@@ -902,6 +904,14 @@ function createRSCStream(rscName, data) {
902
904
  });
903
905
  }
904
906
 
907
+ // src/runtime/patterns.ts
908
+ var REMOTE_COMPONENT_REGEX = /(?<prefix>.*?)\[(?<bundle>[^\]]+)\](?:%20| )(?<id>.+)/;
909
+ var NEXT_BUNDLE_PATH_RE = /\/_next\/\[.+\](?:%20| )/;
910
+ var DOUBLE_SLASH_RE = /(?<!:)\/\//g;
911
+ function collapseDoubleSlashes(path) {
912
+ return path.replace(DOUBLE_SLASH_RE, "/");
913
+ }
914
+
905
915
  // src/runtime/turbopack/patterns.ts
906
916
  var REMOTE_SHARED_MARKER_RE = /(?:self|[a-z])\.TURBOPACK_REMOTE_SHARED/;
907
917
  var REMOTE_SHARED_ASSIGNMENT_RE = /\.TURBOPACK_REMOTE_SHARED=await (?:__turbopack_context__|[a-z])\.A\((?<sharedModuleId>[0-9]+)\)/;
@@ -1491,7 +1501,7 @@ async function setupWebpackRuntime(runtime, scripts = [], url = new URL(location
1491
1501
  }
1492
1502
  }
1493
1503
  if (runtime === RUNTIME_TURBOPACK) {
1494
- await Promise.all(
1504
+ const results = await Promise.allSettled(
1495
1505
  scripts.map((script) => {
1496
1506
  if (script.src) {
1497
1507
  return self.__webpack_chunk_load__?.(script.src, bundle);
@@ -1499,6 +1509,14 @@ async function setupWebpackRuntime(runtime, scripts = [], url = new URL(location
1499
1509
  return Promise.resolve(void 0);
1500
1510
  })
1501
1511
  );
1512
+ for (const result of results) {
1513
+ if (result.status === "rejected") {
1514
+ logWarn(
1515
+ "WebpackRuntime",
1516
+ `Initial chunk load failed: ${String(result.reason)}`
1517
+ );
1518
+ }
1519
+ }
1502
1520
  }
1503
1521
  const coreShared = {
1504
1522
  react: async () => (await import("react")).default,
@@ -1915,11 +1933,7 @@ function bindResolveClientUrl(prop, remoteSrc) {
1915
1933
  function useResolveClientUrl(prop, urlHref) {
1916
1934
  const { resolveClientUrl: contextValue } = useRemoteComponentsContext();
1917
1935
  const raw = prop ?? contextValue;
1918
- const bound = useMemo(
1919
- () => bindResolveClientUrl(raw, urlHref),
1920
- [raw, urlHref]
1921
- );
1922
- return { bound, raw };
1936
+ return useMemo(() => bindResolveClientUrl(raw, urlHref), [raw, urlHref]);
1923
1937
  }
1924
1938
 
1925
1939
  // src/host/react/hooks/use-shadow-root.ts
@@ -1985,7 +1999,7 @@ function getRemoteComponentHtml(html) {
1985
1999
  return ssrRemoteComponentContainer.innerHTML;
1986
2000
  }
1987
2001
  const remoteComponentContainer = temp.querySelectorAll(
1988
- `div[data-bundle][data-route][data-runtime][id^="__vercel_remote_component"],div[data-bundle][data-route],div#__next,remote-component:not([src])`
2002
+ `div[data-bundle][data-route][data-runtime][id^="${DEFAULT_COMPONENT_NAME}"],div[data-bundle][data-route],div#__next,remote-component:not([src])`
1989
2003
  );
1990
2004
  if (remoteComponentContainer.length > 0) {
1991
2005
  return `${Array.from(temp.querySelectorAll("link,script")).map((link) => link.outerHTML).join("")}${Array.from(remoteComponentContainer).map((container) => container.outerHTML).join("")}`;
@@ -2002,7 +2016,7 @@ function ConsumeRemoteComponent({
2002
2016
  mode = "open",
2003
2017
  reset,
2004
2018
  credentials: credentialsProp,
2005
- name: nameProp = "__vercel_remote_component",
2019
+ name: nameProp = DEFAULT_COMPONENT_NAME,
2006
2020
  shared: sharedProp,
2007
2021
  children,
2008
2022
  onBeforeLoad,
@@ -2025,10 +2039,7 @@ function ConsumeRemoteComponent({
2025
2039
  null
2026
2040
  );
2027
2041
  const url = useMemo2(() => getClientOrServerUrl(src, DUMMY_FALLBACK), [src]);
2028
- const { bound: resolveClientUrl } = useResolveClientUrl(
2029
- resolveClientUrlProp,
2030
- url.href
2031
- );
2042
+ const resolveClientUrl = useResolveClientUrl(resolveClientUrlProp, url.href);
2032
2043
  const id = url.origin === (typeof location !== "undefined" ? location.origin : DUMMY_FALLBACK) ? url.pathname : url.href;
2033
2044
  const keySuffix = `${escapeString(id)}_${escapeString(
2034
2045
  data?.name ?? name
@@ -2345,10 +2356,7 @@ function ConsumeRemoteComponent({
2345
2356
  };
2346
2357
  return {
2347
2358
  src: new URL(
2348
- `${prefix ?? ""}${path}`.replace(
2349
- /(?<char>[^:])(?<double>\/\/)/g,
2350
- "$1/"
2351
- ),
2359
+ collapseDoubleSlashes(`${prefix ?? ""}${path}`),
2352
2360
  url
2353
2361
  ).href
2354
2362
  };