datocms-plugin-record-bin 2.0.0 → 3.0.0

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 (47) hide show
  1. package/README.md +113 -11
  2. package/build/assets/index-BnrW9Ts8.js +15 -0
  3. package/build/assets/index-aWCW2c0n.css +1 -0
  4. package/build/index.html +13 -1
  5. package/index.html +12 -0
  6. package/package.json +24 -18
  7. package/src/entrypoints/BinOutlet.tsx +262 -37
  8. package/src/entrypoints/ConfigScreen.tsx +939 -38
  9. package/src/entrypoints/ErrorModal.tsx +86 -2
  10. package/src/index.tsx +73 -28
  11. package/src/react-app-env.d.ts +1 -1
  12. package/src/types/types.ts +36 -8
  13. package/src/utils/binCleanup.test.ts +107 -0
  14. package/src/utils/binCleanup.ts +71 -23
  15. package/src/utils/debugLogger.ts +27 -0
  16. package/src/utils/deployProviders.test.ts +33 -0
  17. package/src/utils/deployProviders.ts +28 -0
  18. package/src/utils/getDeploymentUrlFromParameters.test.ts +26 -0
  19. package/src/utils/getDeploymentUrlFromParameters.ts +21 -0
  20. package/src/utils/getRuntimeMode.test.ts +57 -0
  21. package/src/utils/getRuntimeMode.ts +23 -0
  22. package/src/utils/lambdaLessCapture.test.ts +218 -0
  23. package/src/utils/lambdaLessCapture.ts +160 -0
  24. package/src/utils/lambdaLessCleanup.test.ts +125 -0
  25. package/src/utils/lambdaLessCleanup.ts +69 -0
  26. package/src/utils/lambdaLessRestore.test.ts +248 -0
  27. package/src/utils/lambdaLessRestore.ts +159 -0
  28. package/src/utils/recordBinModel.ts +108 -0
  29. package/src/utils/recordBinPayload.test.ts +103 -0
  30. package/src/utils/recordBinPayload.ts +136 -0
  31. package/src/utils/recordBinWebhook.test.ts +253 -0
  32. package/src/utils/recordBinWebhook.ts +305 -0
  33. package/src/utils/render.tsx +17 -8
  34. package/src/utils/restoreError.test.ts +112 -0
  35. package/src/utils/restoreError.ts +221 -0
  36. package/src/utils/verifyLambdaHealth.test.ts +248 -0
  37. package/src/utils/verifyLambdaHealth.ts +422 -0
  38. package/vite.config.ts +11 -0
  39. package/build/asset-manifest.json +0 -13
  40. package/build/static/css/main.10f29737.css +0 -2
  41. package/build/static/css/main.10f29737.css.map +0 -1
  42. package/build/static/js/main.53795e3b.js +0 -3
  43. package/build/static/js/main.53795e3b.js.LICENSE.txt +0 -47
  44. package/build/static/js/main.53795e3b.js.map +0 -1
  45. package/src/entrypoints/InstallationModal.tsx +0 -107
  46. package/src/entrypoints/PreInstallConfig.tsx +0 -28
  47. package/src/utils/attemptVercelInitialization.ts +0 -16
@@ -0,0 +1 @@
1
+ @font-face{font-display:auto;font-family:colfax-web;font-style:normal;font-weight:700;src:url(https://use.typekit.net/af/522c51/00000000000000003b9acde6/27/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n5&v=3) format("woff2"),url(https://use.typekit.net/af/522c51/00000000000000003b9acde6/27/d?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n5&v=3) format("woff"),url(https://use.typekit.net/af/522c51/00000000000000003b9acde6/27/a?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n5&v=3) format("opentype")}@font-face{font-display:auto;font-family:colfax-web;font-style:italic;font-weight:700;src:url(https://use.typekit.net/af/4e71b3/00000000000000003b9acde7/27/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=i5&v=3) format("woff2"),url(https://use.typekit.net/af/4e71b3/00000000000000003b9acde7/27/d?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=i5&v=3) format("woff"),url(https://use.typekit.net/af/4e71b3/00000000000000003b9acde7/27/a?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=i5&v=3) format("opentype")}@font-face{font-display:auto;font-family:colfax-web;font-style:normal;font-weight:500;src:url(https://use.typekit.net/af/522c51/00000000000000003b9acde6/27/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n5&v=3) format("woff2"),url(https://use.typekit.net/af/522c51/00000000000000003b9acde6/27/d?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n5&v=3) format("woff"),url(https://use.typekit.net/af/522c51/00000000000000003b9acde6/27/a?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n5&v=3) format("opentype")}@font-face{font-display:auto;font-family:colfax-web;font-style:italic;font-weight:500;src:url(https://use.typekit.net/af/4e71b3/00000000000000003b9acde7/27/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=i5&v=3) format("woff2"),url(https://use.typekit.net/af/4e71b3/00000000000000003b9acde7/27/d?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=i5&v=3) format("woff"),url(https://use.typekit.net/af/4e71b3/00000000000000003b9acde7/27/a?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=i5&v=3) format("opentype")}@font-face{font-display:auto;font-family:colfax-web;font-style:normal;font-weight:400;src:url(https://use.typekit.net/af/bac079/00000000000000003b9acde4/27/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n4&v=3) format("woff2"),url(https://use.typekit.net/af/bac079/00000000000000003b9acde4/27/d?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n4&v=3) format("woff"),url(https://use.typekit.net/af/bac079/00000000000000003b9acde4/27/a?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=n4&v=3) format("opentype")}@font-face{font-display:auto;font-family:colfax-web;font-style:italic;font-weight:400;src:url(https://use.typekit.net/af/c1cc04/00000000000000003b9acde5/27/l?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=i4&v=3) format("woff2"),url(https://use.typekit.net/af/c1cc04/00000000000000003b9acde5/27/d?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=i4&v=3) format("woff"),url(https://use.typekit.net/af/c1cc04/00000000000000003b9acde5/27/a?primer=f592e0a4b9356877842506ce344308576437e4f677d7c9b78ca2162e6cad991a&fvd=i4&v=3) format("opentype")}html{font-size:16px;height:auto}body,html{margin:0;padding:0}._button_474wk_1{-webkit-appearance:none;-moz-appearance:none;background-color:transparent;border:0;border-radius:4px;box-sizing:border-box;color:var(--base-body-color);cursor:pointer;display:inline-block;font-family:inherit;font-weight:var(--font-weight-bold);line-height:inherit;opacity:1;text-decoration:none;transition:all .3s var(--material-ease);vertical-align:middle;white-space:nowrap}._button_474wk_1:focus,._button_474wk_1:hover{opacity:.8}._button_474wk_1:active{opacity:.7}._disabled_474wk_30{cursor:not-allowed}._buttonType-muted_474wk_34{background-color:var(--light-color);color:var(--accent-color)}._buttonType-muted_474wk_34._disabled_474wk_30{background-color:var(--light-bg-color);color:#0003}._buttonType-muted_474wk_34._disabled_474wk_30:active,._buttonType-muted_474wk_34._disabled_474wk_30:focus,._buttonType-muted_474wk_34._disabled_474wk_30:hover{color:#0003}._buttonType-primary_474wk_50{background-color:var(--accent-color);color:#fff}._buttonType-primary_474wk_50:active,._buttonType-primary_474wk_50:focus,._buttonType-primary_474wk_50:hover{color:#fff}._buttonType-primary_474wk_50._disabled_474wk_30{background-color:var(--disabled-bg-color);color:#0003}._buttonType-primary_474wk_50._disabled_474wk_30:active,._buttonType-primary_474wk_50._disabled_474wk_30:focus,._buttonType-primary_474wk_50._disabled_474wk_30:hover{color:#0003}._buttonType-negative_474wk_71,._buttonType-negative_474wk_71:active,._buttonType-negative_474wk_71:focus,._buttonType-negative_474wk_71:hover{background-color:var(--alert-color);color:#fff}._buttonType-negative_474wk_71._disabled_474wk_30{background-color:var(--disabled-bg-color);color:#0003}._buttonSize-xxs_474wk_88{font-size:1em;padding:.5em .8em}._buttonSize-xs_474wk_93{font-size:1em;padding:.6em .8em}._buttonSize-s_474wk_98{font-size:1em;padding:.7em 1em}._buttonSize-m_474wk_103{font-size:1.1em;padding:.7em 1em}._buttonSize-l_474wk_108{font-size:1.2em;padding:.7em 1em}._buttonSize-xl_474wk_113{font-size:1.2em;padding:1em}._fullWidth_474wk_118{display:block;text-align:center;width:100%}._button__leftIcon_474wk_124,._button__rightIcon_474wk_125{display:inline-block;line-height:.6;vertical-align:middle}._button__leftIcon_474wk_124 svg,._button__rightIcon_474wk_125 svg{fill:var(--accent-color)}._button__leftIcon_474wk_124{margin-right:.5em}._button__leftIcon_474wk_124:last-child{margin-right:0}._button__rightIcon_474wk_125{margin-left:.5em}._button__rightIcon_474wk_125:first-child{margin-left:0}._Button_1h1w1_1{justify-column:center;align-items:center;-webkit-appearance:none;-moz-appearance:none;background-color:#fff;border:0;border-right:1px solid var(--border-color);border:1px solid var(--border-color);border-left-width:0;box-sizing:border-box;color:var(--base-body-color);color:rgba(var(--base-body-color-rgb-components,.6));cursor:pointer;display:flex;font-family:inherit;font-size:inherit;line-height:inherit;padding:7px 13px}._Button_1h1w1_1:hover{background-color:var(--light-bg-color)}._Button_1h1w1_1 svg{fill:var(--light-body-color)}._Button--s_1h1w1_32{padding:3px 10px}._Button--disabled_1h1w1_36{color:var(--light-body-color);cursor:not-allowed}._Button--disabled_1h1w1_36:hover{background:#fff}._Button--selected_1h1w1_45{background-color:var(--accent-color);border-color:var(--accent-color);color:#fff}._Button--selected_1h1w1_45 svg{fill:#fff}._Button--selected_1h1w1_45:hover{background-color:var(--accent-color)}._Button--selected_1h1w1_45._Button--disabled_1h1w1_36,._Button--selected_1h1w1_45:hover{background-color:rgba(var(--accent-color-rgb-components),.8);border-color:rgba(var(--accent-color-rgb-components),.8)}._Button_1h1w1_1:first-child{border-left-width:1px;border-radius:4px 0 0 4px}._Button_1h1w1_1:last-child{border-radius:0 4px 4px 0}._Group_10mj4_1{align-items:stretch;background-color:#fff;display:flex;overflow:hidden}._themeVariables_2dr1w_1{--base-body-color-rgb-components:52,54,58;--base-body-color:rgb(var(--base-body-color-rgb-components));--light-body-color-rgb-components:132,132,132;--light-body-color:rgb(var(--light-body-color-rgb-components));--placeholder-body-color-rgb-components:198,198,198;--placeholder-body-color:rgb(var(--placeholder-body-color-rgb-components));--light-bg-color-rgb-components:245,245,245;--light-bg-color:rgb(var(--light-bg-color-rgb-components));--lighter-bg-color-rgb-components:248,248,248;--lighter-bg-color:rgb(var(--lighter-bg-color-rgb-components));--disabled-bg-color-rgb-components:237,237,237;--disabled-bg-color:rgb(var(--disabled-bg-color-rgb-components));--border-color-rgb-components:240,240,240;--border-color:rgb(var(--border-color-rgb-components));--darker-border-color-rgb-components:215,215,215;--darker-border-color:rgb(var(--darker-border-color-rgb-components));--alert-color-rgb-components:255,94,73;--alert-color:rgb(var(--alert-color-rgb-components));--warning-color-rgb-components:255,215,0;--warning-color:rgb(var(--warning-color-rgb-components));--notice-color-rgb-components:70,215,0;--notice-color:rgb(var(--notice-color-rgb-components));--warning-bg-color-rgb-components:255,255,229;--warning-bg-color:rgb(var(--warning-bg-color-rgb-components));--add-color-rgb-components:76,176,109;--add-color:rgb(var(--add-color-rgb-components));--remove-color-rgb-components:235,87,106;--remove-color:rgb(var(--remove-color-rgb-components));--base-font-family:"colfax-web","Roboto","Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;--monospaced-font-family:"Roboto Mono","Menlo","Bitstream Vera Sans Mono",Consolas,Courier,monospace;--font-weight-bold:500;--spacing-s:.375rem;--spacing-m:.75rem;--spacing-l:1.5rem;--spacing-xl:2.25rem;--spacing-xxl:3.75rem;--spacing-xxxl:6rem;--negative-spacing-s:-.375rem;--negative-spacing-m:-.75rem;--negative-spacing-l:-1.5rem;--negative-spacing-xl:-2.25rem;--negative-spacing-xxl:-3.75rem;--negative-spacing-xxxl:-6rem;--font-size-xxs:.6875rem;--font-size-xs:.75rem;--font-size-s:.875rem;--font-size-m:.9375rem;--font-size-l:1.0625rem;--font-size-xl:1.1875rem;--font-size-xxl:1.5625rem;--font-size-xxxl:1.875rem;--material-ease:cubic-bezier(.55,0,.1,1);--inertial-ease:cubic-bezier(.19,1,.22,1)}._canvas_2dr1w_70{-webkit-text-size-adjust:100%;text-rendering:optimizeLegibility;color:var(--base-body-color);font-family:var(--base-font-family);font-size:var(--font-size-m);line-height:1.5}._inspector_u6041_1{margin:var(--spacing-l) 0}._panel_u6041_5{border-bottom:1px solid var(--border-color)}._panelHandle_u6041_9{all:inherit;background:var(--light-bg-color);border:0;box-sizing:border-box;cursor:pointer;display:block;font-weight:var(--font-weight-bold);padding:5px 10px;width:100%}._panelHandle_u6041_9:hover{background:var(--lighter-bg-color)}._panelBody_u6041_25{border-left:1px solid var(--border-color);border-right:1px solid var(--border-color);padding:20px}._groupDescription_u6041_31{font-size:var(--font-size-s);line-height:1.2;margin-bottom:20px}._propertyGroup_u6041_37{border:1px solid var(--border-color);border-radius:5px}._propertyOrMethod_u6041_42{border-bottom:1px solid var(--border-color);line-height:1.2}._propertyOrMethod_u6041_42 p{margin:0}._propertyOrMethodBody_u6041_51{padding:15px}._propertyOrMethodExample_u6041_55{position:relative}._propertyOrMethodExample_u6041_55 pre{background:var(--light-bg-color);font-size:.8em;margin:0;max-height:240px;overflow:auto;padding:15px}._propertyOrMethodName_u6041_68{color:var(--light-body-color);display:block;font-family:var(--monospaced-font-family);font-size:.9em;font-weight:700;margin-bottom:5px;text-decoration:none}._propertyOrMethodName_u6041_68:hover{text-decoration:underline}._propertyOrMethodExampleActions_u6041_82{background:#fff;display:flex;padding:15px}._propertyOrMethodExampleActions_u6041_82>*{margin-right:10px}._fieldError_qi0xk_1{color:var(--alert-color);font-size:var(--font-size-xs);line-height:1.2;margin-top:var(--spacing-s)}._FieldGroup_uz9ju_1{display:block}._FieldGroup__item_uz9ju_5{display:block;margin-bottom:var(--spacing-l)}._FieldGroup__item_uz9ju_5:last-child{margin-bottom:0}._fieldHint_1avon_1{color:var(--light-body-color);font-size:var(--font-size-xs);line-height:1.2;margin-top:var(--spacing-s)}._fieldHint_1avon_1 a{color:inherit}._fieldHint_1avon_1 a:hover{text-decoration:none}._Form_5qspp_1,._Form__item_5qspp_5{display:block}._Form__item_5qspp_5:last-child{margin-bottom:0}._Form__item--default_5qspp_13{margin-bottom:var(--spacing-l)}._Form__item--condensed_5qspp_17{margin-bottom:var(--spacing-m)}._formLabel_tcjrv_1{align-items:center;color:var(--light-body-color);display:flex;margin-bottom:var(--spacing-s)}._formLabel_tcjrv_1:hover ._formLabel__code_tcjrv_8{opacity:1}._formLabel--error_tcjrv_14{color:var(--alert-color)}._formLabel__label_tcjrv_18{flex:1}._formLabel__code_tcjrv_8{font-family:var(--monospaced-font-family);font-size:.95em;opacity:0;position:absolute;transition:opacity .2s var(--material-ease)}._hotKey_1eko8_1{align-items:center;display:inline-flex;gap:15px}._label_1eko8_7{align-items:center;display:flex}._keys_1eko8_12{align-items:center;display:inline-flex;gap:4px}._hotKeyKey_1eko8_18{background:var(--light-color);border-radius:3px;padding:5px 8px}._Section_zh95u_1{position:relative}._Section--highlighted_zh95u_5:before{animation:_pageContentSectionHighligh_zh95u_1 4s ease-in-out .25s forwards;border-radius:4px;box-shadow:0 0 0 4px var(--accent-color);content:"";inset:-20px -30px;pointer-events:none;position:absolute;z-index:10}._Section__header_zh95u_19{margin-bottom:var(--spacing-l);margin-left:var(--negative-spacing-l);margin-right:var(--negative-spacing-l);position:relative}._Section__header_zh95u_19:before{background-color:var(--border-color);content:"";display:block;height:1px;left:0;position:absolute;right:0;top:50%;z-index:1}._Section__title_zh95u_38{align-items:center;background-color:#fff;display:inline-flex;font-size:var(--font-size-l);font-weight:var(--font-weight-bold);margin-left:var(--spacing-m);margin-right:var(--spacing-l);padding-left:var(--spacing-m);padding-right:var(--spacing-m);position:relative;z-index:2}._Section__arrow_zh95u_55{all:initial;align-self:stretch;cursor:pointer;margin-right:.3em;width:15px}._Section__arrow_zh95u_55:before{border-bottom:6px solid transparent;border-left:6px solid var(--base-body-color);border-top:6px solid transparent;content:"";height:0;left:14px;margin-top:-6px;position:absolute;top:50%;transform-origin:50% 50%;transition:transform .2s ease-out;width:0}._Section__arrow_zh95u_55:hover:before{opacity:.7}._Section__arrow--is-open_zh95u_82:before{transform:rotate(90deg)}@keyframes _pageContentSectionHighligh_zh95u_1{0%{box-shadow:0 0 0 4px var(--accent-color),0 0 0 4px rgba(var(--accent-color-rgb-components),.7)}15%{box-shadow:0 0 0 4px var(--accent-color),0 0 0 80px transparent}75%{box-shadow:0 0 0 4px var(--accent-color),0 0 0 80px transparent}to{box-shadow:0 0 0 4px transparent,0 0 0 80px transparent}}._SidebarPanel_4uwco_1{border-bottom:1px solid var(--border-color)}._SidebarPanel__header_4uwco_5{align-items:center;-webkit-appearance:none;-moz-appearance:none;background-color:#fff;background-color:var(--light-bg-color);border:0;box-sizing:border-box;color:var(--base-body-color);cursor:pointer;display:flex;font-family:inherit;font-size:inherit;line-height:inherit;padding:0;text-align:left;-webkit-user-select:none;user-select:none;width:100%}._SidebarPanel__header_4uwco_5:focus,._SidebarPanel__header_4uwco_5:hover{background-color:var(--lighter-bg-color)}._SidebarPanel__header__title_4uwco_30{flex:1;font-weight:500;padding:0 20px}._SidebarPanel__header__chevron_4uwco_36{align-items:center;display:flex;padding:13px 15px}._SidebarPanel__content_4uwco_42{background-color:#fff;padding:20px}._SidebarPanel__content--no-padding_4uwco_47{padding:0}._Spinner--inline_oumod_1{display:inline-block;position:relative;vertical-align:middle}._Spinner--centered_oumod_7{left:50%;position:absolute;top:50%}._Spinner__bar_oumod_13{animation:_Spinner__spin_oumod_1 1.2s linear infinite;background-color:var(--light-body-color);height:14%;left:-20%;position:absolute;top:0;width:40%}@keyframes _Spinner__spin_oumod_1{0%{opacity:1}to{opacity:.15}}._switchField__flex_16z4j_1{align-items:center;display:flex}._switchField__switchInput_16z4j_6{width:55px}._switchField__label_16z4j_10{color:var(--base-body-color);flex:1;line-height:1.1;line-height:20px;margin-bottom:0;pointer-events:none;-moz-user-select:text;-ms-user-select:text;-webkit-user-select:text;user-select:text}._switchField__below_16z4j_22{margin-left:55px;margin-top:var(--spacing-s)}._switchInput__inner_1knbg_1{color:#fff;font-size:12px;left:24px;position:absolute}._switchInput_1knbg_1{background-color:#ccc;border:1px solid #ccc;border-radius:20px;box-sizing:border-box;cursor:pointer;display:inline-block;height:22px;line-height:20px;position:relative;transition:all .3s cubic-bezier(.35,0,.25,1);vertical-align:middle;width:44px}._switchInput_1knbg_1:after{animation-duration:.3s;animation-name:_switchInput__off_1knbg_1;animation-timing-function:cubic-bezier(.35,0,.25,1);background-color:#fff;border-radius:50%;box-shadow:0 2px 5px #00000042;content:" ";cursor:pointer;height:18px;left:2px;position:absolute;top:1px;transform:scale(1);transition:left .3s cubic-bezier(.35,0,.25,1);width:18px}._switchInput_1knbg_1:focus:after,._switchInput_1knbg_1:hover{animation-name:_switchInput__on_1knbg_1;transform:scale(1.1)}._switchInput__checked_1knbg_47{background-color:var(--accent-color);border:1px solid var(--accent-color)}._switchInput__checked_1knbg_47 ._switchInput__inner_1knbg_1{left:6px}._switchInput__checked_1knbg_47:after{left:22px}._switchInput__disabled_1knbg_60{background:#ccc;border-color:#ccc;cursor:no-drop}._switchInput__disabled_1knbg_60:after{animation-name:_none_1knbg_1;background:#9e9e9e;cursor:no-drop}._switchInput__disabled_1knbg_60:focus:after,._switchInput__disabled_1knbg_60:hover{animation-name:_none_1knbg_1;transform:scale(1)}@keyframes _switchInput__on_1knbg_1{0%{transform:scale(1)}50%{transform:scale(1.25)}to{transform:scale(1.1)}}@keyframes _switchInput__off_1knbg_1{0%{transform:scale(1.1)}to{transform:scale(1)}}._TextInput_x2oj2_1{appearance:none;background-image:none;border:1px solid var(--border-color);border-radius:0;box-sizing:border-box;display:block;font-family:inherit;font-size:var(--font-size-m);padding:10px;resize:none;transition:border .2s var(--material-ease);width:100%}._TextInput_x2oj2_1::placeholder{color:var(--placeholder-body-color)}._TextInput_x2oj2_1:hover{border-color:var(--darker-border-color)}._TextInput_x2oj2_1:focus{border-color:var(--accent-color);box-shadow:0 0 0 3px var(--semi-transparent-accent-color);outline:0}._TextInput--monospaced_x2oj2_30{font-family:var(--monospaced-font-family);font-size:var(--font-size-s)}._TextInput--disabled_x2oj2_35{background:var(--lighter-bg-color);border-color:var(--border-color);color:var(--light-body-color)}._TextInput--error_x2oj2_41,._TextInput--error_x2oj2_41:focus,._TextInput--error_x2oj2_41:hover{border-color:var(--alert-color)}._TextInput--error_x2oj2_41:focus{box-shadow:0 0 0 3px rgba(var(--alert-color-rgb-components),.2)}._TextareaInput_1wnu9_1{appearance:none;background-image:none;border:1px solid var(--border-color);border-radius:0;box-sizing:border-box;display:block;font-family:inherit;font-size:var(--font-size-m);padding:10px;resize:none;transition:border .2s var(--material-ease);width:100%}._TextareaInput_1wnu9_1::placeholder{color:var(--placeholder-body-color)}._TextareaInput_1wnu9_1:hover{border-color:var(--darker-border-color)}._TextareaInput_1wnu9_1:focus{border-color:var(--accent-color);box-shadow:0 0 0 3px var(--semi-transparent-accent-color);outline:0}._TextareaInput--monospaced_1wnu9_30{font-family:var(--monospaced-font-family);font-size:var(--font-size-s)}._TextareaInput--disabled_1wnu9_35{background:var(--lighter-bg-color);border-color:var(--border-color);color:var(--light-body-color)}._TextareaInput--error_1wnu9_41,._TextareaInput--error_1wnu9_41:focus,._TextareaInput--error_1wnu9_41:hover{border-color:var(--alert-color)}._TextareaInput--error_1wnu9_41:focus{box-shadow:0 0 0 3px rgba(var(--alert-color-rgb-components),.2)}._Button_fy6g6_1{align-items:center;-webkit-appearance:none;-moz-appearance:none;background-color:transparent;border:0;border-left:1px solid var(--border-color);border-right:1px solid var(--border-color);box-sizing:border-box;color:var(--base-body-color);cursor:pointer;display:flex;font-family:inherit;justify-content:center;line-height:inherit;min-height:49px;padding:0;width:49px}._Button_fy6g6_1:focus,._Button_fy6g6_1:hover{background-color:var(--light-bg-color)}._Button_fy6g6_1:first-child{border-left:0}._Button_fy6g6_1:last-child{border-right:0}@media screen and (min-width:380px){._Button_fy6g6_1{min-height:59px;width:59px}._Button_fy6g6_1 svg{font-size:20px}}._Stack_1nnzo_1{align-items:center;display:flex;flex:1;flex-direction:row;justify-content:center;padding:10px 15px}._Stack--s_1nnzo_10{padding-bottom:var(--spacing-s);padding-top:var(--spacing-s)}._Stack--l_1nnzo_15{padding-left:var(--spacing-xl);padding-right:var(--spacing-xl)}._Title_1dx5n_1{font-size:var(--font-size-l);font-weight:500;line-height:1.1;margin-right:15px}@media screen and (min-width:380px){._Title_1dx5n_1{font-size:var(--font-size-xl)}}._Toolbar_1cwb8_1{align-items:stretch;border-bottom:1px solid var(--border-color);border-top:1px solid var(--border-color);display:flex;position:relative}._Toolbar_1cwb8_1:first-child{border-top-width:0}._Toolbar_1cwb8_1:last-child{border-bottom-width:0}._tooltip_3z5rn_1{word-wrap:break-word;background:#fff;border-radius:4px;box-shadow:0 1px 9px #0003;-webkit-hyphens:auto;hyphens:auto;max-width:400px;overflow-wrap:break-word;padding:10px 15px}._Dropdown_nie0g_1{position:relative}._Dropdown__spacer_nie0g_5{inset:0;pointer-events:none;position:absolute}._Dropdown__menu__search_nie0g_11{border-bottom:1px solid var(--border-color);padding:7px}._Dropdown__menu__search__input_nie0g_16{appearance:none;background-image:none;border:1px solid var(--border-color);border-radius:3px;box-sizing:border-box;display:block;font-family:inherit;font-size:.9em;padding:8px;resize:none;transition:border .2s var(--material-ease);width:100%}._Dropdown__menu__search__input_nie0g_16::placeholder{color:var(--placeholder-body-color)}._Dropdown__menu__search__input_nie0g_16:hover{border-color:var(--darker-border-color)}._Dropdown__menu__search__input_nie0g_16:focus{border-color:var(--accent-color);box-shadow:0 0 0 3px var(--semi-transparent-accent-color);outline:0}._Dropdown__menu-container_nie0g_45{position:fixed;visibility:hidden}._Dropdown__menu_nie0g_11{background-color:#fff;border-radius:4px;box-shadow:0 3px 10px #0003;margin-bottom:var(--spacing-xl);margin-top:10px;min-width:200px;padding:1px 0;text-align:initial}._Dropdown__menu__inner_nie0g_61{margin:7px 0}._Dropdown__menu__group__title_nie0g_65{background-color:var(--light-bg-color);color:var(--light-body-color);font-size:var(--font-size-xs);padding:5px 15px 3px;text-transform:uppercase}._Dropdown__menu__group__content_nie0g_73{margin:8px 0}._Dropdown__menu__text_nie0g_77{color:var(--light-body-color);display:block;line-height:1.2;padding:4px 15px;position:relative;text-align:left}._Dropdown__menu__option_nie0g_86{align-items:center;color:var(--base-body-color);display:flex;padding:4px 15px;position:relative;text-align:left;text-decoration:none;white-space:nowrap}._Dropdown__menu__option_nie0g_86:focus,._Dropdown__menu__option_nie0g_86:hover{background-color:var(--light-bg-color)}._Dropdown__menu__option_nie0g_86>a{color:inherit;display:block;text-decoration:none}._Dropdown__menu__option--is-selected_nie0g_108{background-color:var(--light-bg-color)}._Dropdown__menu__option--is-disabled_nie0g_112{opacity:.5}._Dropdown__menu__option--is-disabled_nie0g_112 ._Dropdown__menu__option__content_nie0g_115{cursor:not-allowed}._Dropdown__menu__option--is-dangerous_nie0g_120{color:var(--alert-color)}._Dropdown__menu__option--is-dangerous_nie0g_120 svg{fill:var(--alert-color)}._Dropdown__menu__option--is-dangerous_nie0g_120:focus,._Dropdown__menu__option--is-dangerous_nie0g_120:hover{background-color:var(--alert-color);color:#fff}._Dropdown__menu__option--is-dangerous_nie0g_120:focus svg,._Dropdown__menu__option--is-dangerous_nie0g_120:hover svg{fill:#fff}._Dropdown__menu__option--is-active_nie0g_138{font-weight:500}._Dropdown__menu__option--is-active_nie0g_138:focus,._Dropdown__menu__option--is-active_nie0g_138:hover{background-color:none}._Dropdown__menu__option--is-invalid_nie0g_151,._Dropdown__menu__option--is-valid_nie0g_147{padding-left:35px}._Dropdown__menu__option--is-invalid_nie0g_151:before{background-color:var(--alert-color);border-radius:4px;content:"";font-size:12px;height:8px;left:15px;position:absolute;top:50%;transform:translateY(-50%);width:8px}._Dropdown__menu__option__content_nie0g_115{-webkit-appearance:none;-moz-appearance:none;background-color:transparent;border:0;box-sizing:border-box;color:inherit;cursor:pointer;flex:1;font-family:inherit;font-size:inherit;font-weight:inherit;line-height:inherit;padding:0;text-align:left}._Dropdown__menu__option__content_nie0g_115>svg{fill:var(--light-body-color);color:var(--light-body-color);display:inline-block;font-size:13px;padding-right:8px;vertical-align:middle}._Dropdown__menu__option__icons_nie0g_194{align-items:center;display:flex;padding-left:15px}._Dropdown__menu__option__icon_nie0g_194{-webkit-appearance:none;-moz-appearance:none;background-color:transparent;border:0;box-sizing:border-box;color:var(--base-body-color);color:var(--light-body-color);cursor:pointer;display:inline-block;font-family:inherit;font-size:inherit;font-size:13px;line-height:inherit;line-height:10px;opacity:0;padding:3px;position:relative;top:-1px;transition:transform .1s ease-in-out;width:100%;width:auto}._Dropdown__menu__option_nie0g_86:focus ._Dropdown__menu__option__icon_nie0g_194,._Dropdown__menu__option_nie0g_86:hover ._Dropdown__menu__option__icon_nie0g_194{opacity:1}._Dropdown__menu__option__icon_nie0g_194:focus,._Dropdown__menu__option__icon_nie0g_194:hover{transform:scale(1.2)}._Dropdown__menu__option__icon_nie0g_194 svg{fill:var(--light-body-color)}._Dropdown__menu__option__icon--delete_nie0g_241{color:var(--alert-color)}._Dropdown__menu__option__icon--delete_nie0g_241 svg{fill:var(--alert-color)}._Dropdown__menu__separator_nie0g_248{background-color:var(--border-color);height:1px;margin:8px 0}._Dropdown__menu_nie0g_11{overflow-y:auto}._SplitViewPane_1cl1f_1{height:100%;overflow-y:auto;position:absolute;width:100%}._SplitViewSash_tds51_1{align-items:center;background-color:rgba(var(--light-color-components),0);display:flex;height:100%;justify-content:center;position:absolute;top:0;transition:background-color .2s .15s;width:100%;z-index:2}._SplitViewSash--dragging_tds51_15,._SplitViewSash_tds51_1:hover{background-color:var(--light-color)}._SplitViewSash--dragging_tds51_15:has(._SplitViewSash__content_tds51_19:hover),._SplitViewSash_tds51_1:hover:has(._SplitViewSash__content_tds51_19:hover){background-color:transparent}._SplitViewSash--no-resize_tds51_24{pointer-events:none}._SplitViewSash--vertical_tds51_28{cursor:col-resize}._SplitViewSash--horizontal_tds51_32{cursor:row-resize}._SplitViewSash__content_tds51_19{cursor:pointer;left:50%;padding:15px 0;pointer-events:auto;position:absolute;top:50%;transform:translate(-50%,-50%)}._SplitViewSash__content__button_tds51_46{align-items:center;background:#fff;border:1px solid var(--border-color);border-radius:6px;color:var(--light-body-color);display:flex;font-size:10px;height:20px;justify-content:center;width:20px;z-index:2}._SplitViewSash__content__button_tds51_46 svg{fill:currentColor;display:block}._SplitViewSash__content_tds51_19:hover ._SplitViewSash__content__button_tds51_46{background:var(--light-bg-color);color:var(--base-body-color)}._SplitView_1oi17_1{flex:1;height:100%;position:relative;width:100%}._SplitView--dragging_1oi17_8._SplitView--vertical_1oi17_8{cursor:col-resize}._SplitView--dragging_1oi17_8._SplitView--horizontal_1oi17_12{cursor:row-resize}._SplitView--disable-select_1oi17_16{-webkit-user-select:none;user-select:none}._SplitView--disable-select_1oi17_16 ._SplitViewPane_1oi17_20{pointer-events:none}._VerticalSplitPane__expand_80tii_1{background:#fff;cursor:pointer;inset:0;position:absolute;transition:all .3s cubic-bezier(.55,0,.1,1)}._VerticalSplitPane__expand_80tii_1:hover{animation:_VerticalSplitPane__expand_80tii_1 .6s cubic-bezier(.55,0,.1,1);background:var(--light-bg-color)}._VerticalSplitPane__expand_80tii_1._VerticalSplitPane__expand_80tii_1{border-right:1px solid var(--border-color);transform-origin:left}._VerticalSplitPane__expand_80tii_1._VerticalSplitPane__expand--right_80tii_25{border-left:1px solid var(--border-color);transform-origin:right}@keyframes _VerticalSplitPane__expand_80tii_1{0%{transform:scaleX(1)}50%{transform:scaleX(2)}to{transform:scaleX(1)}}._VerticalSplitPaneOverlay_80tii_42{background:linear-gradient(180deg,#30302f80,#30302f4d);height:100%;inset:0;overflow:hidden;position:absolute;z-index:12}._VerticalSplitPaneOverlay__primary_80tii_58{inset:0;position:absolute;z-index:1}._VerticalSplitPaneOverlay__primary--left_80tii_67{margin-right:20px}._VerticalSplitPaneOverlay__primary--right_80tii_71{margin-left:20px}._VerticalSplitPaneOverlay__secondary_80tii_75{background:#fff;bottom:0;box-shadow:0 0 15px #0006;position:absolute;top:0}._VerticalSplitPaneOverlay__secondary--left_80tii_83{left:0}._VerticalSplitPaneOverlay__secondary--right_80tii_87{right:0}._VerticalSplitPaneOverlay__sash_80tii_91{height:100%;position:absolute;top:0}._VerticalSplitPaneOverlay__secondary--left_80tii_83 ._VerticalSplitPaneOverlay__sash_80tii_91{right:0}._VerticalSplitPaneOverlay__secondary--right_80tii_87 ._VerticalSplitPaneOverlay__sash_80tii_91{left:0}
package/build/index.html CHANGED
@@ -1 +1,13 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><script defer="defer" src="./static/js/main.53795e3b.js"></script><link href="./static/css/main.10f29737.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <script type="module" crossorigin src="./assets/index-BnrW9Ts8.js"></script>
7
+ <link rel="stylesheet" crossorigin href="./assets/index-aWCW2c0n.css">
8
+ </head>
9
+ <body>
10
+ <noscript>You need to enable JavaScript to run this app.</noscript>
11
+ <div id="root"></div>
12
+ </body>
13
+ </html>
package/index.html ADDED
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ </head>
7
+ <body>
8
+ <noscript>You need to enable JavaScript to run this app.</noscript>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/index.tsx"></script>
11
+ </body>
12
+ </html>
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "datocms-plugin-record-bin",
3
- "version": "2.0.0",
4
- "private": false,
5
- "homepage": "https://github.com/marcelofinamorvieira/datocms-plugin-record-bin",
3
+ "homepage": "https://github.com/datocms/plugins/tree/master/record-bin#readme",
4
+ "version": "3.0.0",
5
+ "author": "DatoCMS <support@datocms.com>",
6
6
  "description": "A plugin to manage and create a backup of deleted records in DatoCMS",
7
7
  "keywords": [
8
8
  "datocms",
@@ -16,24 +16,28 @@
16
16
  "previewImage": "docs/demo.mp4",
17
17
  "coverImage": "docs/cover.png",
18
18
  "entryPoint": "build/index.html",
19
- "permissions": []
19
+ "permissions": [
20
+ "currentUserAccessToken"
21
+ ]
20
22
  },
21
23
  "dependencies": {
22
- "@types/node": "^16.11.52",
23
- "@types/react": "^17.0.48",
24
- "@types/react-dom": "^17.0.17",
25
- "datocms-plugin-sdk": "^0.6.1",
26
- "datocms-react-ui": "^0.6.0",
27
- "react": "^18.2.0",
28
- "react-dom": "^18.2.0",
29
- "react-scripts": "5.0.1",
30
- "typescript": "^4.7.4"
24
+ "@datocms/cma-client-browser": "^5.3.0",
25
+ "@types/node": "^25.3.0",
26
+ "@types/react": "^18.3.28",
27
+ "@types/react-dom": "^18.3.7",
28
+ "datocms-plugin-sdk": "^2.1.1",
29
+ "datocms-react-ui": "^2.1.4",
30
+ "react": "^18.3.1",
31
+ "react-dom": "^18.3.1",
32
+ "typescript": "^5.9.3"
31
33
  },
32
34
  "scripts": {
33
- "start": "cross-env BROWSER='none' PUBLIC_URL='/' react-scripts start",
34
- "build": "cross-env PUBLIC_URL='.' react-scripts build",
35
- "test": "react-scripts test",
36
- "eject": "react-scripts eject",
35
+ "dev": "vite",
36
+ "start": "npm run dev",
37
+ "build": "vite build",
38
+ "preview": "vite preview",
39
+ "test": "vitest run --passWithNoTests",
40
+ "test:watch": "vitest --passWithNoTests",
37
41
  "prepublishOnly": "npm run build"
38
42
  },
39
43
  "eslintConfig": {
@@ -54,6 +58,8 @@
54
58
  ]
55
59
  },
56
60
  "devDependencies": {
57
- "cross-env": "^7.0.3"
61
+ "@vitejs/plugin-react": "^5.1.0",
62
+ "vite": "^7.1.12",
63
+ "vitest": "^4.0.0"
58
64
  }
59
65
  }
@@ -1,34 +1,224 @@
1
1
  import { RenderItemFormOutletCtx } from "datocms-plugin-sdk";
2
- import { Button, Canvas, FieldGroup, Form, Section } from "datocms-react-ui";
2
+ import { Button, Canvas, FieldGroup, Form } from "datocms-react-ui";
3
3
  import { useState } from "react";
4
4
  import { errorObject } from "../types/types";
5
+ import { createDebugLogger, isDebugEnabled } from "../utils/debugLogger";
6
+ import { getDeploymentUrlFromParameters } from "../utils/getDeploymentUrlFromParameters";
7
+ import { getRuntimeMode } from "../utils/getRuntimeMode";
8
+ import {
9
+ isLambdaLessRestoreError,
10
+ restoreRecordWithoutLambda,
11
+ } from "../utils/lambdaLessRestore";
12
+ import {
13
+ buildRestoreErrorPayload,
14
+ isRestoreSuccessResponse,
15
+ parseJsonStringSafely,
16
+ } from "../utils/restoreError";
17
+
18
+ const getPrimaryErrorLine = (restorationError: errorObject): string => {
19
+ const details = restorationError.simplifiedError.details;
20
+ const detailMessage =
21
+ (typeof details.field === "string" && details.field) ||
22
+ (Array.isArray(details.extraneous_attributes) &&
23
+ details.extraneous_attributes.length > 0
24
+ ? details.extraneous_attributes.join(", ")
25
+ : undefined) ||
26
+ (typeof details.message === "string" && details.message) ||
27
+ "No details available";
28
+
29
+ return `${restorationError.simplifiedError.code || "UNKNOWN"}: ${detailMessage}`;
30
+ };
31
+
32
+ const getSecondaryErrorLine = (
33
+ restorationError: errorObject
34
+ ): string | undefined => {
35
+ const details = restorationError.simplifiedError.details;
36
+
37
+ if (typeof details.code === "string" && details.code.trim().length > 0) {
38
+ return `Details code: ${details.code}`;
39
+ }
40
+
41
+ if (typeof details.field_id === "string" && details.field_id.trim().length > 0) {
42
+ return `Field ID: ${details.field_id}`;
43
+ }
44
+
45
+ if (
46
+ typeof details.field_label === "string" &&
47
+ details.field_label.trim().length > 0
48
+ ) {
49
+ return `Field label: ${details.field_label}`;
50
+ }
51
+
52
+ return undefined;
53
+ };
5
54
 
6
55
  const BinOutlet = ({ ctx }: { ctx: RenderItemFormOutletCtx }) => {
56
+ const debugLogger = createDebugLogger(
57
+ isDebugEnabled(ctx.plugin.attributes.parameters),
58
+ "BinOutlet"
59
+ );
7
60
  const [isLoading, setLoading] = useState(false);
8
61
  const [error, setError] = useState<errorObject>();
9
62
 
10
63
  const restorationHandler = async () => {
64
+ debugLogger.log("Starting record restoration", {
65
+ itemId: ctx.item?.id,
66
+ itemTypeId: ctx.itemType.id,
67
+ });
68
+ setError(undefined);
11
69
  setLoading(true);
12
70
 
13
- const parsedBody = JSON.parse(ctx.formValues.record_body as string);
14
- parsedBody.trashRecordID = ctx.item!.id;
15
- const requestBody = JSON.stringify(parsedBody);
71
+ const runtimeMode = getRuntimeMode(ctx.plugin.attributes.parameters);
72
+ const rawRecordBody = ctx.formValues.record_body;
73
+ let parsedRecordBody: unknown;
74
+ try {
75
+ parsedRecordBody =
76
+ typeof rawRecordBody === "string" ? JSON.parse(rawRecordBody) : rawRecordBody;
77
+ } catch (error) {
78
+ debugLogger.warn("Could not parse record_body JSON before restore", error);
79
+ await ctx.alert("The record body is not valid JSON.");
80
+ return;
81
+ }
16
82
 
17
83
  try {
18
- const restoreResponse = await fetch(
19
- ctx.plugin.attributes.parameters.vercelURL as string,
20
- {
21
- method: "POST",
22
- body: requestBody,
23
- headers: { Accept: "*/*", "Content-Type": "application/json" },
84
+ if (runtimeMode === "lambda") {
85
+ const deploymentURL = getDeploymentUrlFromParameters(
86
+ ctx.plugin.attributes.parameters
87
+ );
88
+
89
+ if (!deploymentURL) {
90
+ debugLogger.warn("Missing deployment URL while restoring record");
91
+ await ctx.alert("The plugin deployment URL is missing.");
92
+ return;
24
93
  }
25
- );
26
- const parsedResponse = await restoreResponse.json();
27
- if (restoreResponse.status !== 200) {
28
- setError(parsedResponse.error);
29
- throw new Error();
94
+
95
+ if (
96
+ !parsedRecordBody ||
97
+ typeof parsedRecordBody !== "object" ||
98
+ Array.isArray(parsedRecordBody)
99
+ ) {
100
+ debugLogger.warn("record_body must be a JSON object for lambda restore");
101
+ await ctx.alert("The record body is not a valid restore payload.");
102
+ return;
103
+ }
104
+
105
+ if (!ctx.item?.id) {
106
+ debugLogger.warn("Missing trash record id in lambda restore");
107
+ await ctx.alert("Could not determine the trash record to remove.");
108
+ return;
109
+ }
110
+
111
+ const parsedBody = {
112
+ ...(parsedRecordBody as Record<string, unknown>),
113
+ trashRecordID: ctx.item.id,
114
+ };
115
+ const requestBody = JSON.stringify(parsedBody);
116
+ debugLogger.log("Sending restoration request to lambda", {
117
+ deploymentURL,
118
+ requestBodyLength: requestBody.length,
119
+ });
120
+
121
+ let restoreResponse: Response;
122
+ try {
123
+ restoreResponse = await fetch(deploymentURL, {
124
+ method: "POST",
125
+ body: requestBody,
126
+ headers: { Accept: "*/*", "Content-Type": "application/json" },
127
+ });
128
+ } catch (error) {
129
+ const restorationError = buildRestoreErrorPayload(error);
130
+ setError(restorationError);
131
+ debugLogger.warn(
132
+ "Restoration request failed before receiving response",
133
+ restorationError
134
+ );
135
+ await ctx.alert("The record could not be restored!");
136
+ return;
137
+ }
138
+
139
+ debugLogger.log("Received restoration response", {
140
+ status: restoreResponse.status,
141
+ });
142
+
143
+ const rawResponseBody = await restoreResponse.text();
144
+ const parsedResponse = parseJsonStringSafely(rawResponseBody);
145
+
146
+ if (!restoreResponse.ok || !isRestoreSuccessResponse(parsedResponse)) {
147
+ const fallbackMessage = restoreResponse.ok
148
+ ? "The restore API returned an invalid success payload."
149
+ : `The restore API request failed with status ${restoreResponse.status}.`;
150
+ const trimmedResponseBody = rawResponseBody.trim();
151
+ const restorationError = buildRestoreErrorPayload(
152
+ parsedResponse ?? rawResponseBody,
153
+ {
154
+ fullErrorPayload:
155
+ trimmedResponseBody.length > 0 ? trimmedResponseBody : undefined,
156
+ fallbackMessage,
157
+ }
158
+ );
159
+ setError(restorationError);
160
+ debugLogger.warn("Restoration request failed", {
161
+ status: restoreResponse.status,
162
+ restorationError,
163
+ });
164
+ await ctx.alert("The record could not be restored!");
165
+ return;
166
+ }
167
+
168
+ debugLogger.log("Restoration succeeded", {
169
+ restoredRecordId: parsedResponse.restoredRecord.id,
170
+ restoredModelId: parsedResponse.restoredRecord.modelID,
171
+ });
172
+ ctx.notice("The record has been successfully restored!");
173
+ ctx.navigateTo(
174
+ "/editor/item_types/" +
175
+ parsedResponse.restoredRecord.modelID +
176
+ "/items/" +
177
+ parsedResponse.restoredRecord.id
178
+ );
179
+ return;
30
180
  }
31
181
 
182
+ if (!ctx.item?.id) {
183
+ debugLogger.warn("Missing trash record id in lambda-less restore");
184
+ await ctx.alert("Could not determine the trash record to remove.");
185
+ return;
186
+ }
187
+
188
+ debugLogger.log("Sending restoration request through Lambda-less runtime");
189
+ let parsedResponse:
190
+ | Awaited<ReturnType<typeof restoreRecordWithoutLambda>>
191
+ | undefined;
192
+ try {
193
+ parsedResponse = await restoreRecordWithoutLambda({
194
+ currentUserAccessToken: ctx.currentUserAccessToken,
195
+ fallbackEnvironment: ctx.environment,
196
+ recordBody: parsedRecordBody,
197
+ trashRecordID: ctx.item.id,
198
+ });
199
+ } catch (error) {
200
+ if (isLambdaLessRestoreError(error)) {
201
+ setError(error.restorationError);
202
+ debugLogger.warn(
203
+ "Lambda-less restoration request failed",
204
+ error.restorationError
205
+ );
206
+ await ctx.alert("The record could not be restored!");
207
+ return;
208
+ }
209
+
210
+ throw error;
211
+ }
212
+
213
+ if (!parsedResponse) {
214
+ return;
215
+ }
216
+
217
+ debugLogger.log("Lambda-less restoration succeeded", {
218
+ restoredRecordId: parsedResponse.restoredRecord.id,
219
+ restoredModelId: parsedResponse.restoredRecord.modelID,
220
+ });
221
+
32
222
  ctx.notice("The record has been successfully restored!");
33
223
  ctx.navigateTo(
34
224
  "/editor/item_types/" +
@@ -37,43 +227,40 @@ const BinOutlet = ({ ctx }: { ctx: RenderItemFormOutletCtx }) => {
37
227
  parsedResponse.restoredRecord.id
38
228
  );
39
229
  } catch (error) {
40
- setLoading(false);
230
+ debugLogger.error("Restoration flow failed", error);
41
231
  await ctx.alert("The record could not be restored!");
232
+ } finally {
233
+ setLoading(false);
42
234
  }
43
235
  };
44
236
 
45
237
  const errorModalHandler = async () => {
238
+ if (!error) {
239
+ return;
240
+ }
241
+
242
+ debugLogger.log("Opening restoration error modal");
46
243
  await ctx.openModal({
47
244
  id: "errorModal",
48
245
  title: "Restoration error",
49
246
  width: "l",
50
- parameters: { errorPayload: error!.fullErrorPayload },
247
+ parameters: { errorPayload: error.fullErrorPayload },
51
248
  });
52
249
  };
53
250
 
251
+ const primaryErrorLine = error ? getPrimaryErrorLine(error) : undefined;
252
+ const secondaryErrorLine = error ? getSecondaryErrorLine(error) : undefined;
253
+ const restorationErrorBoxStyle = {
254
+ border: "1px solid rgba(var(--alert-color-rgb-components), 0.5)",
255
+ borderRadius: "6px",
256
+ background: "rgba(var(--alert-color-rgb-components), 0.08)",
257
+ padding: "var(--spacing-m)",
258
+ marginBottom: "var(--spacing-m)",
259
+ };
260
+
54
261
  return (
55
262
  <Canvas ctx={ctx}>
56
263
  <Form>
57
- <FieldGroup>
58
- {error && (
59
- <Section title="Restoration error">
60
- <p>Couldn't restore the record because of the following error:</p>
61
- <p>
62
- {error.simplifiedError.code}:{" "}
63
- {error.simplifiedError.details.field ||
64
- error.simplifiedError.details.extraneous_attributes}
65
- </p>
66
- <p>{error.simplifiedError.details.code}</p>
67
- <Button onClick={errorModalHandler}>
68
- See full restoration error report
69
- </Button>
70
- <p>
71
- You can manually correct the errors on the record body, save the
72
- record, and re-attempt to restore it.
73
- </p>
74
- </Section>
75
- )}
76
- </FieldGroup>
77
264
  <FieldGroup>
78
265
  <Button
79
266
  buttonType={isLoading ? "muted" : "primary"}
@@ -84,6 +271,44 @@ const BinOutlet = ({ ctx }: { ctx: RenderItemFormOutletCtx }) => {
84
271
  Restore record ♻️
85
272
  </Button>
86
273
  </FieldGroup>
274
+ <FieldGroup>
275
+ {error && (
276
+ <div style={restorationErrorBoxStyle}>
277
+ <h3
278
+ style={{
279
+ marginTop: 0,
280
+ marginBottom: "var(--spacing-s)",
281
+ fontSize: "var(--font-size-m)",
282
+ }}
283
+ >
284
+ Restoration error
285
+ </h3>
286
+ <p style={{ marginTop: 0, marginBottom: "var(--spacing-s)" }}>
287
+ Restoring a record is an optimistic operation. If this record
288
+ references linked records that no longer exist, restoration can fail.
289
+ </p>
290
+ <p style={{ marginTop: 0, marginBottom: "var(--spacing-s)" }}>
291
+ In case of API errors, inspect the details below and update the{" "}
292
+ <code>record_body</code> JSON if needed before trying again.
293
+ </p>
294
+ <p style={{ marginTop: 0, marginBottom: "var(--spacing-s)" }}>
295
+ {primaryErrorLine}
296
+ </p>
297
+ {secondaryErrorLine && (
298
+ <p style={{ marginTop: 0, marginBottom: "var(--spacing-s)" }}>
299
+ {secondaryErrorLine}
300
+ </p>
301
+ )}
302
+ <Button onClick={errorModalHandler}>
303
+ See full restoration error report
304
+ </Button>
305
+ <p style={{ marginTop: "var(--spacing-s)", marginBottom: 0 }}>
306
+ You can manually correct the JSON payload, save the record, and
307
+ re-attempt restoration.
308
+ </p>
309
+ </div>
310
+ )}
311
+ </FieldGroup>
87
312
  </Form>
88
313
  </Canvas>
89
314
  );