@webiny/app-admin 6.5.0-beta.1 → 6.5.0-beta.2
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.
- package/base/Admin.js +2 -0
- package/base/Admin.js.map +1 -1
- package/base/Base/FieldRenderers/AutoCompleteRenderer.js +1 -0
- package/base/Base/FieldRenderers/AutoCompleteRenderer.js.map +1 -1
- package/base/Base/FieldRenderers/SelectRenderer.js +1 -0
- package/base/Base/FieldRenderers/SelectRenderer.js.map +1 -1
- package/features/debugger/Debugger.d.ts +8 -0
- package/features/debugger/Debugger.js +25 -0
- package/features/debugger/Debugger.js.map +1 -0
- package/features/debugger/DebuggerIndicator.d.ts +16 -0
- package/features/debugger/DebuggerIndicator.js +31 -0
- package/features/debugger/DebuggerIndicator.js.map +1 -0
- package/features/debugger/DebuggerStore.d.ts +55 -0
- package/features/debugger/DebuggerStore.js +65 -0
- package/features/debugger/DebuggerStore.js.map +1 -0
- package/features/debugger/DebuggerView.d.ts +4 -0
- package/features/debugger/DebuggerView.js +49 -0
- package/features/debugger/DebuggerView.js.map +1 -0
- package/features/debugger/GraphQLExtensionsDebugHandler.d.ts +15 -0
- package/features/debugger/GraphQLExtensionsDebugHandler.js +15 -0
- package/features/debugger/GraphQLExtensionsDebugHandler.js.map +1 -0
- package/features/debugger/debuggerLink.d.ts +12 -0
- package/features/debugger/debuggerLink.js +10 -0
- package/features/debugger/debuggerLink.js.map +1 -0
- package/features/debugger/feature.d.ts +1 -0
- package/features/debugger/feature.js +18 -0
- package/features/debugger/feature.js.map +1 -0
- package/features/debugger/index.d.ts +7 -0
- package/features/debugger/index.js +7 -0
- package/features/debugger/permissions.d.ts +68 -0
- package/features/debugger/permissions.js +27 -0
- package/features/debugger/permissions.js.map +1 -0
- package/features/debugger/report.d.ts +7 -0
- package/features/debugger/report.js +22 -0
- package/features/debugger/report.js.map +1 -0
- package/features/debugger/routes.d.ts +4 -0
- package/features/debugger/routes.js +10 -0
- package/features/debugger/routes.js.map +1 -0
- package/features/formModel/createFieldRenderer.js +3 -4
- package/features/formModel/createFieldRenderer.js.map +1 -1
- package/hooks/useHotkeys.js +3 -1
- package/hooks/useHotkeys.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.js +3 -2
- package/package.json +19 -19
package/base/Admin.js
CHANGED
|
@@ -24,6 +24,7 @@ import { SortableFeature } from "../presentation/sortable/index.js";
|
|
|
24
24
|
import { NotificationsRenderer } from "../features/notifications/NotificationsRenderer.js";
|
|
25
25
|
import { CommandPaletteFeature } from "../presentation/commandPalette/index.js";
|
|
26
26
|
import { AdminCommandsFeature } from "../presentation/commandPalette/commands/feature.js";
|
|
27
|
+
import { DebuggerFeature } from "../features/debugger/feature.js";
|
|
27
28
|
const container = createRootContainer();
|
|
28
29
|
const Admin = ({ children, createApolloClient, createLegacyPlugins })=>{
|
|
29
30
|
const uri = process.env.REACT_APP_GRAPHQL_API_URL;
|
|
@@ -40,6 +41,7 @@ const Admin = ({ children, createApolloClient, createLegacyPlugins })=>{
|
|
|
40
41
|
CommandPaletteFeature.register(container);
|
|
41
42
|
AdminCommandsFeature.register(container);
|
|
42
43
|
SortableFeature.register(container);
|
|
44
|
+
DebuggerFeature.register(container);
|
|
43
45
|
const ApolloProvider = createApolloProvider(apolloClient);
|
|
44
46
|
const UIProviders = createUiProviders();
|
|
45
47
|
const UiStateProvider = createUiStateProvider();
|
package/base/Admin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base/Admin.js","sources":["../../src/base/Admin.tsx"],"sourcesContent":["import React from \"react\";\nimport type { Container } from \"@webiny/di\";\nimport { plugins } from \"@webiny/plugins\";\nimport { App, DiContainerProvider } from \"@webiny/app\";\nimport type { ApolloClientFactory } from \"./providers/ApolloProvider.js\";\nimport { createApolloProvider } from \"./providers/ApolloProvider.js\";\nimport { Base } from \"./Base.js\";\nimport { createUiStateProvider } from \"./providers/UiStateProvider.js\";\nimport { createAdminUiStateProvider } from \"./providers/AdminUiStateProvider.js\";\nimport { createUiProviders } from \"./providers/UiProviders.js\";\nimport { createDialogsProvider } from \"~/components/Dialogs/DialogsContext.js\";\nimport { createDrawersProvider } from \"~/components/Drawers/DrawersContext.js\";\nimport { DefaultIcons, IconPickerConfigProvider } from \"~/components/IconPicker/config/index.js\";\nimport { createRootContainer } from \"~/base/createRootContainer.js\";\nimport { WcpProvider } from \"~/presentation/wcp/WcpProvider.js\";\nimport { createTenancyProvider } from \"~/presentation/tenancy/createTenancyProvider.js\";\nimport { TelemetryAdminAppStart } from \"./TelemetryAdminAppStart.js\";\nimport { ApolloClientFeature } from \"~/features/apolloClient/feature.js\";\nimport { SecurityFeature } from \"~/features/security/SecurityFeature.js\";\nimport { FormModelFeature } from \"~/features/formModel/feature.js\";\nimport type { PluginCollection } from \"@webiny/plugins/types.js\";\nimport { AdminConfigPlugin, AdminConfigProvider } from \"~/config/AdminConfig.js\";\nimport { WebinySdkFeature } from \"~/features/webinySdk/feature.js\";\nimport { DateFormatterFeature } from \"~/features/dateFormatter/feature.js\";\nimport { ListPresenterFeature } from \"~/presentation/listPresenter/index.js\";\nimport { SortableFeature } from \"~/presentation/sortable/index.js\";\nimport { NotificationsRenderer } from \"~/features/notifications/NotificationsRenderer.js\";\nimport { CommandPaletteFeature } from \"~/presentation/commandPalette/index.js\";\nimport { AdminCommandsFeature } from \"~/presentation/commandPalette/commands/feature.js\";\n\nexport interface AdminProps {\n createApolloClient: ApolloClientFactory;\n createLegacyPlugins: (container: Container) => PluginCollection;\n children?: React.ReactNode;\n}\n\nconst container = createRootContainer();\n\nexport const Admin = ({ children, createApolloClient, createLegacyPlugins }: AdminProps) => {\n const uri = process.env.REACT_APP_GRAPHQL_API_URL as string;\n const apolloClient = createApolloClient({ uri });\n\n plugins.register(...createLegacyPlugins(container));\n\n ApolloClientFeature.register(container, apolloClient);\n SecurityFeature.register(container);\n DateFormatterFeature.register(container);\n FormModelFeature.register(container);\n WebinySdkFeature.register(container);\n ListPresenterFeature.register(container);\n CommandPaletteFeature.register(container);\n AdminCommandsFeature.register(container);\n SortableFeature.register(container);\n\n const ApolloProvider = createApolloProvider(apolloClient);\n const UIProviders = createUiProviders();\n const UiStateProvider = createUiStateProvider();\n const AdminUiStateProvider = createAdminUiStateProvider();\n const DialogsProvider = createDialogsProvider();\n const DrawersProvider = createDrawersProvider();\n const TenancyProvider = createTenancyProvider();\n\n return (\n <DiContainerProvider container={container}>\n <TelemetryAdminAppStart />\n <ApolloProvider>\n <WcpProvider>\n <App\n plugins={[AdminConfigPlugin]}\n routes={[]}\n providers={[\n AdminConfigProvider,\n UIProviders,\n UiStateProvider,\n DialogsProvider,\n DrawersProvider,\n IconPickerConfigProvider,\n AdminUiStateProvider,\n TenancyProvider\n ]}\n >\n <Base />\n <DefaultIcons />\n <NotificationsRenderer />\n {children}\n </App>\n </WcpProvider>\n </ApolloProvider>\n </DiContainerProvider>\n );\n};\n"],"names":["container","createRootContainer","Admin","children","createApolloClient","createLegacyPlugins","uri","process","apolloClient","plugins","ApolloClientFeature","SecurityFeature","DateFormatterFeature","FormModelFeature","WebinySdkFeature","ListPresenterFeature","CommandPaletteFeature","AdminCommandsFeature","SortableFeature","ApolloProvider","createApolloProvider","UIProviders","createUiProviders","UiStateProvider","createUiStateProvider","AdminUiStateProvider","createAdminUiStateProvider","DialogsProvider","createDialogsProvider","DrawersProvider","createDrawersProvider","TenancyProvider","createTenancyProvider","DiContainerProvider","TelemetryAdminAppStart","WcpProvider","App","AdminConfigPlugin","AdminConfigProvider","IconPickerConfigProvider","Base","DefaultIcons","NotificationsRenderer"],"mappings":"
|
|
1
|
+
{"version":3,"file":"base/Admin.js","sources":["../../src/base/Admin.tsx"],"sourcesContent":["import React from \"react\";\nimport type { Container } from \"@webiny/di\";\nimport { plugins } from \"@webiny/plugins\";\nimport { App, DiContainerProvider } from \"@webiny/app\";\nimport type { ApolloClientFactory } from \"./providers/ApolloProvider.js\";\nimport { createApolloProvider } from \"./providers/ApolloProvider.js\";\nimport { Base } from \"./Base.js\";\nimport { createUiStateProvider } from \"./providers/UiStateProvider.js\";\nimport { createAdminUiStateProvider } from \"./providers/AdminUiStateProvider.js\";\nimport { createUiProviders } from \"./providers/UiProviders.js\";\nimport { createDialogsProvider } from \"~/components/Dialogs/DialogsContext.js\";\nimport { createDrawersProvider } from \"~/components/Drawers/DrawersContext.js\";\nimport { DefaultIcons, IconPickerConfigProvider } from \"~/components/IconPicker/config/index.js\";\nimport { createRootContainer } from \"~/base/createRootContainer.js\";\nimport { WcpProvider } from \"~/presentation/wcp/WcpProvider.js\";\nimport { createTenancyProvider } from \"~/presentation/tenancy/createTenancyProvider.js\";\nimport { TelemetryAdminAppStart } from \"./TelemetryAdminAppStart.js\";\nimport { ApolloClientFeature } from \"~/features/apolloClient/feature.js\";\nimport { SecurityFeature } from \"~/features/security/SecurityFeature.js\";\nimport { FormModelFeature } from \"~/features/formModel/feature.js\";\nimport type { PluginCollection } from \"@webiny/plugins/types.js\";\nimport { AdminConfigPlugin, AdminConfigProvider } from \"~/config/AdminConfig.js\";\nimport { WebinySdkFeature } from \"~/features/webinySdk/feature.js\";\nimport { DateFormatterFeature } from \"~/features/dateFormatter/feature.js\";\nimport { ListPresenterFeature } from \"~/presentation/listPresenter/index.js\";\nimport { SortableFeature } from \"~/presentation/sortable/index.js\";\nimport { NotificationsRenderer } from \"~/features/notifications/NotificationsRenderer.js\";\nimport { CommandPaletteFeature } from \"~/presentation/commandPalette/index.js\";\nimport { AdminCommandsFeature } from \"~/presentation/commandPalette/commands/feature.js\";\nimport { DebuggerFeature } from \"~/features/debugger/feature.js\";\n\nexport interface AdminProps {\n createApolloClient: ApolloClientFactory;\n createLegacyPlugins: (container: Container) => PluginCollection;\n children?: React.ReactNode;\n}\n\nconst container = createRootContainer();\n\nexport const Admin = ({ children, createApolloClient, createLegacyPlugins }: AdminProps) => {\n const uri = process.env.REACT_APP_GRAPHQL_API_URL as string;\n const apolloClient = createApolloClient({ uri });\n\n plugins.register(...createLegacyPlugins(container));\n\n ApolloClientFeature.register(container, apolloClient);\n SecurityFeature.register(container);\n DateFormatterFeature.register(container);\n FormModelFeature.register(container);\n WebinySdkFeature.register(container);\n ListPresenterFeature.register(container);\n CommandPaletteFeature.register(container);\n AdminCommandsFeature.register(container);\n SortableFeature.register(container);\n DebuggerFeature.register(container);\n\n const ApolloProvider = createApolloProvider(apolloClient);\n const UIProviders = createUiProviders();\n const UiStateProvider = createUiStateProvider();\n const AdminUiStateProvider = createAdminUiStateProvider();\n const DialogsProvider = createDialogsProvider();\n const DrawersProvider = createDrawersProvider();\n const TenancyProvider = createTenancyProvider();\n\n return (\n <DiContainerProvider container={container}>\n <TelemetryAdminAppStart />\n <ApolloProvider>\n <WcpProvider>\n <App\n plugins={[AdminConfigPlugin]}\n routes={[]}\n providers={[\n AdminConfigProvider,\n UIProviders,\n UiStateProvider,\n DialogsProvider,\n DrawersProvider,\n IconPickerConfigProvider,\n AdminUiStateProvider,\n TenancyProvider\n ]}\n >\n <Base />\n <DefaultIcons />\n <NotificationsRenderer />\n {children}\n </App>\n </WcpProvider>\n </ApolloProvider>\n </DiContainerProvider>\n );\n};\n"],"names":["container","createRootContainer","Admin","children","createApolloClient","createLegacyPlugins","uri","process","apolloClient","plugins","ApolloClientFeature","SecurityFeature","DateFormatterFeature","FormModelFeature","WebinySdkFeature","ListPresenterFeature","CommandPaletteFeature","AdminCommandsFeature","SortableFeature","DebuggerFeature","ApolloProvider","createApolloProvider","UIProviders","createUiProviders","UiStateProvider","createUiStateProvider","AdminUiStateProvider","createAdminUiStateProvider","DialogsProvider","createDialogsProvider","DrawersProvider","createDrawersProvider","TenancyProvider","createTenancyProvider","DiContainerProvider","TelemetryAdminAppStart","WcpProvider","App","AdminConfigPlugin","AdminConfigProvider","IconPickerConfigProvider","Base","DefaultIcons","NotificationsRenderer"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,MAAMA,YAAYC;AAEX,MAAMC,QAAQ,CAAC,EAAEC,QAAQ,EAAEC,kBAAkB,EAAEC,mBAAmB,EAAc;IACnF,MAAMC,MAAMC,QAAQ,GAAG,CAAC,yBAAyB;IACjD,MAAMC,eAAeJ,mBAAmB;QAAEE;IAAI;IAE9CG,QAAQ,QAAQ,IAAIJ,oBAAoBL;IAExCU,oBAAoB,QAAQ,CAACV,WAAWQ;IACxCG,gBAAgB,QAAQ,CAACX;IACzBY,qBAAqB,QAAQ,CAACZ;IAC9Ba,iBAAiB,QAAQ,CAACb;IAC1Bc,iBAAiB,QAAQ,CAACd;IAC1Be,qBAAqB,QAAQ,CAACf;IAC9BgB,sBAAsB,QAAQ,CAAChB;IAC/BiB,qBAAqB,QAAQ,CAACjB;IAC9BkB,gBAAgB,QAAQ,CAAClB;IACzBmB,gBAAgB,QAAQ,CAACnB;IAEzB,MAAMoB,iBAAiBC,qBAAqBb;IAC5C,MAAMc,cAAcC;IACpB,MAAMC,kBAAkBC;IACxB,MAAMC,uBAAuBC;IAC7B,MAAMC,kBAAkBC;IACxB,MAAMC,kBAAkBC;IACxB,MAAMC,kBAAkBC;IAExB,OAAO,WAAP,GACI,oBAACC,qBAAmBA;QAAC,WAAWlC;qBAC5B,oBAACmC,wBAAsBA,OAAAA,WAAAA,GACvB,oBAACf,gBAAAA,MAAAA,WAAAA,GACG,oBAACgB,aAAWA,MAAAA,WAAAA,GACR,oBAACC,KAAGA;QACA,SAAS;YAACC;SAAkB;QAC5B,QAAQ,EAAE;QACV,WAAW;YACPC;YACAjB;YACAE;YACAI;YACAE;YACAU;YACAd;YACAM;SACH;qBAED,oBAACS,MAAIA,OAAAA,WAAAA,GACL,oBAACC,cAAYA,OAAAA,WAAAA,GACb,oBAACC,uBAAqBA,OACrBxC;AAMzB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base/Base/FieldRenderers/AutoCompleteRenderer.js","sources":["../../../../src/base/Base/FieldRenderers/AutoCompleteRenderer.tsx"],"sourcesContent":["import React from \"react\";\nimport { createFieldRenderer } from \"~/features/formModel/createFieldRenderer.js\";\nimport { AutoComplete } from \"@webiny/admin-ui\";\nimport type { IValueOption } from \"~/features/formModel/index.js\";\n\ndeclare module \"../../../features/formModel/abstractions.js\" {\n interface IFieldRendererRegistry {\n autoComplete: { fieldType: \"text\" | \"number\"; options: true; settings: undefined };\n }\n}\n\nexport const AutoCompleteRenderer = createFieldRenderer(({ field }) => {\n const options: IValueOption[] = field.options ?? [];\n\n return (\n <AutoComplete\n label={field.label}\n hint={field.help}\n placeholder={field.placeholder}\n description={field.description}\n note={field.note}\n value={field.value != null ? String(field.value) : \"\"}\n onValueChange={value => {\n field.onChange(value);\n field.onBlur();\n }}\n required={field.required}\n disabled={field.disabled}\n validation={field.validation}\n options={options.map(opt => ({\n label: opt.label,\n value: String(opt.value),\n disabled: opt.disabled\n }))}\n />\n );\n});\n"],"names":["AutoCompleteRenderer","createFieldRenderer","field","options","AutoComplete","String","value","opt"],"mappings":";;;AAWO,MAAMA,uBAAuBC,oBAAoB,CAAC,EAAEC,KAAK,EAAE;IAC9D,MAAMC,UAA0BD,MAAM,OAAO,IAAI,EAAE;IAEnD,OAAO,WAAP,GACI,oBAACE,cAAYA;QACT,OAAOF,MAAM,KAAK;QAClB,MAAMA,MAAM,IAAI;QAChB,aAAaA,MAAM,WAAW;QAC9B,aAAaA,MAAM,WAAW;QAC9B,MAAMA,MAAM,IAAI;QAChB,OAAOA,AAAe,QAAfA,MAAM,KAAK,GAAWG,OAAOH,MAAM,KAAK,IAAI;QACnD,eAAeI,CAAAA;YACXJ,MAAM,QAAQ,CAACI;YACfJ,MAAM,MAAM;QAChB;QACA,UAAUA,MAAM,QAAQ;
|
|
1
|
+
{"version":3,"file":"base/Base/FieldRenderers/AutoCompleteRenderer.js","sources":["../../../../src/base/Base/FieldRenderers/AutoCompleteRenderer.tsx"],"sourcesContent":["import React from \"react\";\nimport { createFieldRenderer } from \"~/features/formModel/createFieldRenderer.js\";\nimport { AutoComplete } from \"@webiny/admin-ui\";\nimport type { IValueOption } from \"~/features/formModel/index.js\";\n\ndeclare module \"../../../features/formModel/abstractions.js\" {\n interface IFieldRendererRegistry {\n autoComplete: { fieldType: \"text\" | \"number\"; options: true; settings: undefined };\n }\n}\n\nexport const AutoCompleteRenderer = createFieldRenderer(({ field }) => {\n const options: IValueOption[] = field.options ?? [];\n\n return (\n <AutoComplete\n label={field.label}\n hint={field.help}\n placeholder={field.placeholder}\n description={field.description}\n note={field.note}\n value={field.value != null ? String(field.value) : \"\"}\n onValueChange={value => {\n field.onChange(value);\n field.onBlur();\n }}\n required={field.required}\n // Clearing a required field can only leave it invalid, so it gets no reset action.\n displayResetAction={!field.required}\n disabled={field.disabled}\n validation={field.validation}\n options={options.map(opt => ({\n label: opt.label,\n value: String(opt.value),\n disabled: opt.disabled\n }))}\n />\n );\n});\n"],"names":["AutoCompleteRenderer","createFieldRenderer","field","options","AutoComplete","String","value","opt"],"mappings":";;;AAWO,MAAMA,uBAAuBC,oBAAoB,CAAC,EAAEC,KAAK,EAAE;IAC9D,MAAMC,UAA0BD,MAAM,OAAO,IAAI,EAAE;IAEnD,OAAO,WAAP,GACI,oBAACE,cAAYA;QACT,OAAOF,MAAM,KAAK;QAClB,MAAMA,MAAM,IAAI;QAChB,aAAaA,MAAM,WAAW;QAC9B,aAAaA,MAAM,WAAW;QAC9B,MAAMA,MAAM,IAAI;QAChB,OAAOA,AAAe,QAAfA,MAAM,KAAK,GAAWG,OAAOH,MAAM,KAAK,IAAI;QACnD,eAAeI,CAAAA;YACXJ,MAAM,QAAQ,CAACI;YACfJ,MAAM,MAAM;QAChB;QACA,UAAUA,MAAM,QAAQ;QAExB,oBAAoB,CAACA,MAAM,QAAQ;QACnC,UAAUA,MAAM,QAAQ;QACxB,YAAYA,MAAM,UAAU;QAC5B,SAASC,QAAQ,GAAG,CAACI,CAAAA,MAAQ;gBACzB,OAAOA,IAAI,KAAK;gBAChB,OAAOF,OAAOE,IAAI,KAAK;gBACvB,UAAUA,IAAI,QAAQ;YAC1B;;AAGZ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base/Base/FieldRenderers/SelectRenderer.js","sources":["../../../../src/base/Base/FieldRenderers/SelectRenderer.tsx"],"sourcesContent":["import React from \"react\";\nimport { createFieldRenderer } from \"~/features/formModel/createFieldRenderer.js\";\nimport { Select } from \"@webiny/admin-ui\";\nimport type { IValueOption } from \"~/features/formModel/index.js\";\n\ndeclare module \"../../../features/formModel/abstractions.js\" {\n interface IFieldRendererRegistry {\n select: { fieldType: \"text\" | \"number\"; options: true; settings: undefined };\n /** @deprecated Use \"select\" instead. */\n dropdown: { fieldType: \"text\" | \"number\"; options: true; settings: undefined };\n }\n}\n\nconst isValidValue = (value: unknown): value is string | number =>\n typeof value === \"string\" || typeof value === \"number\";\n\nexport const SelectRenderer = createFieldRenderer(({ field }) => {\n const options: IValueOption[] = field.options ?? [];\n\n return (\n <Select\n label={field.label}\n hint={field.help}\n placeholder={field.placeholder}\n description={field.description}\n note={field.note}\n value={isValidValue(field.value) ? String(field.value) : \"\"}\n onChange={value => {\n field.onChange(value);\n field.onBlur();\n }}\n required={field.required}\n disabled={field.disabled}\n validation={field.validation}\n options={options.map(opt => ({\n label: opt.label,\n value: isValidValue(opt.value) ? String(opt.value) : opt.value,\n disabled: opt.disabled\n }))}\n />\n );\n});\n"],"names":["isValidValue","value","SelectRenderer","createFieldRenderer","field","options","Select","String","opt"],"mappings":";;;AAaA,MAAMA,eAAe,CAACC,QAClB,AAAiB,YAAjB,OAAOA,SAAsB,AAAiB,YAAjB,OAAOA;AAEjC,MAAMC,iBAAiBC,oBAAoB,CAAC,EAAEC,KAAK,EAAE;IACxD,MAAMC,UAA0BD,MAAM,OAAO,IAAI,EAAE;IAEnD,OAAO,WAAP,GACI,oBAACE,QAAMA;QACH,OAAOF,MAAM,KAAK;QAClB,MAAMA,MAAM,IAAI;QAChB,aAAaA,MAAM,WAAW;QAC9B,aAAaA,MAAM,WAAW;QAC9B,MAAMA,MAAM,IAAI;QAChB,OAAOJ,aAAaI,MAAM,KAAK,IAAIG,OAAOH,MAAM,KAAK,IAAI;QACzD,UAAUH,CAAAA;YACNG,MAAM,QAAQ,CAACH;YACfG,MAAM,MAAM;QAChB;QACA,UAAUA,MAAM,QAAQ;
|
|
1
|
+
{"version":3,"file":"base/Base/FieldRenderers/SelectRenderer.js","sources":["../../../../src/base/Base/FieldRenderers/SelectRenderer.tsx"],"sourcesContent":["import React from \"react\";\nimport { createFieldRenderer } from \"~/features/formModel/createFieldRenderer.js\";\nimport { Select } from \"@webiny/admin-ui\";\nimport type { IValueOption } from \"~/features/formModel/index.js\";\n\ndeclare module \"../../../features/formModel/abstractions.js\" {\n interface IFieldRendererRegistry {\n select: { fieldType: \"text\" | \"number\"; options: true; settings: undefined };\n /** @deprecated Use \"select\" instead. */\n dropdown: { fieldType: \"text\" | \"number\"; options: true; settings: undefined };\n }\n}\n\nconst isValidValue = (value: unknown): value is string | number =>\n typeof value === \"string\" || typeof value === \"number\";\n\nexport const SelectRenderer = createFieldRenderer(({ field }) => {\n const options: IValueOption[] = field.options ?? [];\n\n return (\n <Select\n label={field.label}\n hint={field.help}\n placeholder={field.placeholder}\n description={field.description}\n note={field.note}\n value={isValidValue(field.value) ? String(field.value) : \"\"}\n onChange={value => {\n field.onChange(value);\n field.onBlur();\n }}\n required={field.required}\n // Clearing a required field can only leave it invalid, so it gets no reset action.\n displayResetAction={!field.required}\n disabled={field.disabled}\n validation={field.validation}\n options={options.map(opt => ({\n label: opt.label,\n value: isValidValue(opt.value) ? String(opt.value) : opt.value,\n disabled: opt.disabled\n }))}\n />\n );\n});\n"],"names":["isValidValue","value","SelectRenderer","createFieldRenderer","field","options","Select","String","opt"],"mappings":";;;AAaA,MAAMA,eAAe,CAACC,QAClB,AAAiB,YAAjB,OAAOA,SAAsB,AAAiB,YAAjB,OAAOA;AAEjC,MAAMC,iBAAiBC,oBAAoB,CAAC,EAAEC,KAAK,EAAE;IACxD,MAAMC,UAA0BD,MAAM,OAAO,IAAI,EAAE;IAEnD,OAAO,WAAP,GACI,oBAACE,QAAMA;QACH,OAAOF,MAAM,KAAK;QAClB,MAAMA,MAAM,IAAI;QAChB,aAAaA,MAAM,WAAW;QAC9B,aAAaA,MAAM,WAAW;QAC9B,MAAMA,MAAM,IAAI;QAChB,OAAOJ,aAAaI,MAAM,KAAK,IAAIG,OAAOH,MAAM,KAAK,IAAI;QACzD,UAAUH,CAAAA;YACNG,MAAM,QAAQ,CAACH;YACfG,MAAM,MAAM;QAChB;QACA,UAAUA,MAAM,QAAQ;QAExB,oBAAoB,CAACA,MAAM,QAAQ;QACnC,UAAUA,MAAM,QAAQ;QACxB,YAAYA,MAAM,UAAU;QAC5B,SAASC,QAAQ,GAAG,CAACG,CAAAA,MAAQ;gBACzB,OAAOA,IAAI,KAAK;gBAChB,OAAOR,aAAaQ,IAAI,KAAK,IAAID,OAAOC,IAAI,KAAK,IAAIA,IAAI,KAAK;gBAC9D,UAAUA,IAAI,QAAQ;YAC1B;;AAGZ"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* No menu entry: the header indicator is the way in, and it downloads the report directly. The
|
|
4
|
+
* panel remains reachable by URL for anyone who wants the namespace filter or the session list.
|
|
5
|
+
*/
|
|
6
|
+
export declare const Debugger: (() => React.JSX.Element) & {
|
|
7
|
+
displayName: string;
|
|
8
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import react, { useEffect } from "react";
|
|
2
|
+
import { observer } from "mobx-react-lite";
|
|
3
|
+
import { AdminConfig } from "../../config/AdminConfig.js";
|
|
4
|
+
import { AdminLayout } from "../../components/AdminLayout.js";
|
|
5
|
+
import { useCanCaptureDebugData } from "./permissions.js";
|
|
6
|
+
import { debuggerStore } from "./DebuggerStore.js";
|
|
7
|
+
import { DebuggerView } from "./DebuggerView.js";
|
|
8
|
+
import { DebuggerRoutes } from "./routes.js";
|
|
9
|
+
const Debugger = observer(()=>{
|
|
10
|
+
const canDebug = useCanCaptureDebugData();
|
|
11
|
+
useEffect(()=>{
|
|
12
|
+
debuggerStore.applyPermission(canDebug);
|
|
13
|
+
}, [
|
|
14
|
+
canDebug
|
|
15
|
+
]);
|
|
16
|
+
return /*#__PURE__*/ react.createElement(AdminConfig, null, /*#__PURE__*/ react.createElement(AdminConfig.Route, {
|
|
17
|
+
route: DebuggerRoutes.Debugger,
|
|
18
|
+
element: /*#__PURE__*/ react.createElement(AdminLayout, {
|
|
19
|
+
title: "Debugger"
|
|
20
|
+
}, /*#__PURE__*/ react.createElement(DebuggerView, null))
|
|
21
|
+
}));
|
|
22
|
+
});
|
|
23
|
+
export { Debugger };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=Debugger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/debugger/Debugger.js","sources":["../../../src/features/debugger/Debugger.tsx"],"sourcesContent":["import React, { useEffect } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport { AdminConfig } from \"~/config/AdminConfig.js\";\nimport { AdminLayout } from \"~/components/AdminLayout.js\";\nimport { useCanCaptureDebugData } from \"./permissions.js\";\nimport { debuggerStore } from \"./DebuggerStore.js\";\nimport { DebuggerView } from \"./DebuggerView.js\";\nimport { DebuggerRoutes } from \"./routes.js\";\n\n/**\n * No menu entry: the header indicator is the way in, and it downloads the report directly. The\n * panel remains reachable by URL for anyone who wants the namespace filter or the session list.\n */\nexport const Debugger = observer(() => {\n const canDebug = useCanCaptureDebugData();\n\n /**\n * Holding the permission is the opt-in, so capture starts on. An identity that has explicitly\n * turned it off keeps it off; one that loses the permission has it turned off for them.\n */\n useEffect(() => {\n debuggerStore.applyPermission(canDebug);\n }, [canDebug]);\n\n return (\n <AdminConfig>\n <AdminConfig.Route\n route={DebuggerRoutes.Debugger}\n element={\n <AdminLayout title={\"Debugger\"}>\n <DebuggerView />\n </AdminLayout>\n }\n />\n </AdminConfig>\n );\n});\n"],"names":["Debugger","observer","canDebug","useCanCaptureDebugData","useEffect","debuggerStore","AdminConfig","DebuggerRoutes","AdminLayout","DebuggerView"],"mappings":";;;;;;;;AAaO,MAAMA,WAAWC,SAAS;IAC7B,MAAMC,WAAWC;IAMjBC,UAAU;QACNC,cAAc,eAAe,CAACH;IAClC,GAAG;QAACA;KAAS;IAEb,OAAO,WAAP,GACI,oBAACI,aAAWA,MAAAA,WAAAA,GACR,oBAACA,YAAY,KAAK;QACd,OAAOC,eAAe,QAAQ;QAC9B,uBACI,oBAACC,aAAWA;YAAC,OAAO;yBAChB,oBAACC,cAAYA;;AAMrC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Says that capture is on, how much has been collected, and offers the two things worth doing
|
|
4
|
+
* about it.
|
|
5
|
+
*
|
|
6
|
+
* Renders nothing when the toggle is off, which is the normal state - the header is not the place to
|
|
7
|
+
* advertise a feature nobody switched on. When it is on it is deliberately hard to miss, because
|
|
8
|
+
* leaving capture running is the mistake it exists to prevent.
|
|
9
|
+
*
|
|
10
|
+
* Clearing matters as much as downloading: an hour of browsing collects a great deal, and everything
|
|
11
|
+
* held is in memory and lands in the next report whether or not it is relevant to the problem being
|
|
12
|
+
* reproduced.
|
|
13
|
+
*/
|
|
14
|
+
export declare const DebuggerIndicator: (() => React.JSX.Element | null) & {
|
|
15
|
+
displayName: string;
|
|
16
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import react from "react";
|
|
2
|
+
import { observer } from "mobx-react-lite";
|
|
3
|
+
import { DropdownMenu, Tag } from "@webiny/admin-ui";
|
|
4
|
+
import { ReactComponent } from "@webiny/icons/bug_report.svg";
|
|
5
|
+
import { debuggerStore } from "./DebuggerStore.js";
|
|
6
|
+
import { downloadReport } from "./report.js";
|
|
7
|
+
const DebuggerIndicator = observer(()=>{
|
|
8
|
+
if (!debuggerStore.enabled) return null;
|
|
9
|
+
const count = debuggerStore.entryCount;
|
|
10
|
+
const hasEntries = count > 0;
|
|
11
|
+
return /*#__PURE__*/ react.createElement(DropdownMenu, {
|
|
12
|
+
trigger: /*#__PURE__*/ react.createElement(Tag, {
|
|
13
|
+
variant: "warning",
|
|
14
|
+
icon: /*#__PURE__*/ react.createElement(ReactComponent, null),
|
|
15
|
+
content: hasEntries ? `Debug mode · ${count}` : "Debug mode",
|
|
16
|
+
title: hasEntries ? `Debug capture is on. ${count} entries collected.` : "Debug capture is on. Nothing collected yet.",
|
|
17
|
+
className: "cursor-pointer"
|
|
18
|
+
})
|
|
19
|
+
}, /*#__PURE__*/ react.createElement(DropdownMenu.Item, {
|
|
20
|
+
text: "Download report",
|
|
21
|
+
disabled: !hasEntries,
|
|
22
|
+
onClick: downloadReport
|
|
23
|
+
}), /*#__PURE__*/ react.createElement(DropdownMenu.Item, {
|
|
24
|
+
text: "Clear collected data",
|
|
25
|
+
disabled: !hasEntries,
|
|
26
|
+
onClick: debuggerStore.clear
|
|
27
|
+
}));
|
|
28
|
+
});
|
|
29
|
+
export { DebuggerIndicator };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=DebuggerIndicator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/debugger/DebuggerIndicator.js","sources":["../../../src/features/debugger/DebuggerIndicator.tsx"],"sourcesContent":["import React from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport { DropdownMenu, Tag } from \"@webiny/admin-ui\";\nimport { ReactComponent as DebuggerIcon } from \"@webiny/icons/bug_report.svg\";\nimport { debuggerStore } from \"./DebuggerStore.js\";\nimport { downloadReport } from \"./report.js\";\n\n/**\n * Says that capture is on, how much has been collected, and offers the two things worth doing\n * about it.\n *\n * Renders nothing when the toggle is off, which is the normal state - the header is not the place to\n * advertise a feature nobody switched on. When it is on it is deliberately hard to miss, because\n * leaving capture running is the mistake it exists to prevent.\n *\n * Clearing matters as much as downloading: an hour of browsing collects a great deal, and everything\n * held is in memory and lands in the next report whether or not it is relevant to the problem being\n * reproduced.\n */\nexport const DebuggerIndicator = observer(() => {\n if (!debuggerStore.enabled) {\n return null;\n }\n\n const count = debuggerStore.entryCount;\n const hasEntries = count > 0;\n\n return (\n <DropdownMenu\n trigger={\n <Tag\n variant={\"warning\"}\n icon={<DebuggerIcon />}\n content={hasEntries ? `Debug mode · ${count}` : \"Debug mode\"}\n title={\n hasEntries\n ? `Debug capture is on. ${count} entries collected.`\n : \"Debug capture is on. Nothing collected yet.\"\n }\n className={\"cursor-pointer\"}\n />\n }\n >\n <DropdownMenu.Item\n text={\"Download report\"}\n disabled={!hasEntries}\n onClick={downloadReport}\n />\n <DropdownMenu.Item\n text={\"Clear collected data\"}\n disabled={!hasEntries}\n onClick={debuggerStore.clear}\n />\n </DropdownMenu>\n );\n});\n"],"names":["DebuggerIndicator","observer","debuggerStore","count","hasEntries","DropdownMenu","Tag","DebuggerIcon","downloadReport"],"mappings":";;;;;;AAmBO,MAAMA,oBAAoBC,SAAS;IACtC,IAAI,CAACC,cAAc,OAAO,EACtB,OAAO;IAGX,MAAMC,QAAQD,cAAc,UAAU;IACtC,MAAME,aAAaD,QAAQ;IAE3B,OAAO,WAAP,GACI,oBAACE,cAAYA;QACT,uBACI,oBAACC,KAAGA;YACA,SAAS;YACT,oBAAM,oBAACC,gBAAYA;YACnB,SAASH,aAAa,CAAC,aAAa,EAAED,OAAO,GAAG;YAChD,OACIC,aACM,CAAC,qBAAqB,EAAED,MAAM,mBAAmB,CAAC,GAClD;YAEV,WAAW;;qBAInB,oBAACE,aAAa,IAAI;QACd,MAAM;QACN,UAAU,CAACD;QACX,SAASI;sBAEb,oBAACH,aAAa,IAAI;QACd,MAAM;QACN,UAAU,CAACD;QACX,SAASF,cAAc,KAAK;;AAI5C"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export interface IDebugEntry {
|
|
2
|
+
seq: number;
|
|
3
|
+
namespace: string;
|
|
4
|
+
timestamp: number;
|
|
5
|
+
elapsed: number;
|
|
6
|
+
data: unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface IDebugPayload {
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
requestId?: string;
|
|
11
|
+
url?: string;
|
|
12
|
+
namespaces?: string[];
|
|
13
|
+
operations?: string[];
|
|
14
|
+
entries?: IDebugEntry[];
|
|
15
|
+
bytes?: number;
|
|
16
|
+
dropped?: number;
|
|
17
|
+
truncated?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface ICollectedSession extends IDebugPayload {
|
|
20
|
+
collectedAt: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Holds the debug toggle and the sessions collected from responses.
|
|
24
|
+
*
|
|
25
|
+
* The toggle persists across reloads, because reproducing a bug often involves one. The collected
|
|
26
|
+
* sessions deliberately do not: they hold customer data, and leaving that in browser storage after
|
|
27
|
+
* the session is a worse exposure than the response itself, which is transient.
|
|
28
|
+
*/
|
|
29
|
+
export declare class DebuggerStore {
|
|
30
|
+
private _enabled;
|
|
31
|
+
private _namespaces;
|
|
32
|
+
private _sessions;
|
|
33
|
+
constructor();
|
|
34
|
+
/**
|
|
35
|
+
* Turns capture on for an identity that holds the permission, unless they have already turned it
|
|
36
|
+
* off themselves.
|
|
37
|
+
*
|
|
38
|
+
* Holding `dev-tools.debug` is the opt-in: the permission is granted deliberately, for a support
|
|
39
|
+
* session, and nobody should then have to find a second switch before the thing they were asked
|
|
40
|
+
* to reproduce is captured.
|
|
41
|
+
*/
|
|
42
|
+
applyPermission: (canCapture: boolean) => void;
|
|
43
|
+
get enabled(): boolean;
|
|
44
|
+
get namespaces(): string;
|
|
45
|
+
get sessions(): ICollectedSession[];
|
|
46
|
+
get entryCount(): number;
|
|
47
|
+
setEnabled: (enabled: boolean) => void;
|
|
48
|
+
setNamespaces: (namespaces: string) => void;
|
|
49
|
+
collect: (payload: IDebugPayload) => void;
|
|
50
|
+
clear: () => void;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A single store instance, read by the Apollo link on every request and by the panel.
|
|
54
|
+
*/
|
|
55
|
+
export declare const debuggerStore: DebuggerStore;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { makeAutoObservable } from "mobx";
|
|
2
|
+
const TOGGLE_STORAGE_KEY = "webiny_debugger_enabled";
|
|
3
|
+
const NAMESPACES_STORAGE_KEY = "webiny_debugger_namespaces";
|
|
4
|
+
const readStorage = (key)=>{
|
|
5
|
+
try {
|
|
6
|
+
return window.localStorage.getItem(key);
|
|
7
|
+
} catch {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
const writeStorage = (key, value)=>{
|
|
12
|
+
try {
|
|
13
|
+
if (null === value) return void window.localStorage.removeItem(key);
|
|
14
|
+
window.localStorage.setItem(key, value);
|
|
15
|
+
} catch {}
|
|
16
|
+
};
|
|
17
|
+
class DebuggerStore {
|
|
18
|
+
constructor(){
|
|
19
|
+
this._sessions = [];
|
|
20
|
+
this.applyPermission = (canCapture)=>{
|
|
21
|
+
if (!canCapture) {
|
|
22
|
+
this._enabled = false;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (null === readStorage(TOGGLE_STORAGE_KEY)) this._enabled = true;
|
|
26
|
+
};
|
|
27
|
+
this.setEnabled = (enabled)=>{
|
|
28
|
+
this._enabled = enabled;
|
|
29
|
+
writeStorage(TOGGLE_STORAGE_KEY, enabled ? "true" : "false");
|
|
30
|
+
};
|
|
31
|
+
this.setNamespaces = (namespaces)=>{
|
|
32
|
+
this._namespaces = namespaces;
|
|
33
|
+
writeStorage(NAMESPACES_STORAGE_KEY, namespaces || null);
|
|
34
|
+
};
|
|
35
|
+
this.collect = (payload)=>{
|
|
36
|
+
if (!payload.entries?.length) return;
|
|
37
|
+
this._sessions.push({
|
|
38
|
+
...payload,
|
|
39
|
+
collectedAt: Date.now()
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
this.clear = ()=>{
|
|
43
|
+
this._sessions = [];
|
|
44
|
+
};
|
|
45
|
+
this._enabled = "true" === readStorage(TOGGLE_STORAGE_KEY);
|
|
46
|
+
this._namespaces = readStorage(NAMESPACES_STORAGE_KEY) || "*";
|
|
47
|
+
makeAutoObservable(this);
|
|
48
|
+
}
|
|
49
|
+
get enabled() {
|
|
50
|
+
return this._enabled;
|
|
51
|
+
}
|
|
52
|
+
get namespaces() {
|
|
53
|
+
return this._namespaces;
|
|
54
|
+
}
|
|
55
|
+
get sessions() {
|
|
56
|
+
return this._sessions;
|
|
57
|
+
}
|
|
58
|
+
get entryCount() {
|
|
59
|
+
return this._sessions.reduce((total, session)=>total + (session.entries?.length || 0), 0);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const debuggerStore = new DebuggerStore();
|
|
63
|
+
export { DebuggerStore, debuggerStore };
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=DebuggerStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/debugger/DebuggerStore.js","sources":["../../../src/features/debugger/DebuggerStore.ts"],"sourcesContent":["import { makeAutoObservable } from \"mobx\";\n\nconst TOGGLE_STORAGE_KEY = \"webiny_debugger_enabled\";\nconst NAMESPACES_STORAGE_KEY = \"webiny_debugger_namespaces\";\n\nexport interface IDebugEntry {\n seq: number;\n namespace: string;\n timestamp: number;\n elapsed: number;\n data: unknown;\n}\n\nexport interface IDebugPayload {\n enabled: boolean;\n requestId?: string;\n url?: string;\n namespaces?: string[];\n operations?: string[];\n entries?: IDebugEntry[];\n bytes?: number;\n dropped?: number;\n truncated?: boolean;\n}\n\nexport interface ICollectedSession extends IDebugPayload {\n collectedAt: number;\n}\n\nconst readStorage = (key: string): string | null => {\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n};\n\nconst writeStorage = (key: string, value: string | null): void => {\n try {\n if (value === null) {\n window.localStorage.removeItem(key);\n return;\n }\n window.localStorage.setItem(key, value);\n } catch {\n // Private windows and blocked site data both throw. The toggle simply will not persist.\n }\n};\n\n/**\n * Holds the debug toggle and the sessions collected from responses.\n *\n * The toggle persists across reloads, because reproducing a bug often involves one. The collected\n * sessions deliberately do not: they hold customer data, and leaving that in browser storage after\n * the session is a worse exposure than the response itself, which is transient.\n */\nexport class DebuggerStore {\n private _enabled: boolean;\n private _namespaces: string;\n private _sessions: ICollectedSession[] = [];\n\n public constructor() {\n /**\n * Absent means \"no choice made yet\", which is different from an explicit \"off\" - the first\n * decides whether the permission turns capture on, the second must survive it.\n */\n this._enabled = readStorage(TOGGLE_STORAGE_KEY) === \"true\";\n this._namespaces = readStorage(NAMESPACES_STORAGE_KEY) || \"*\";\n makeAutoObservable(this);\n }\n\n /**\n * Turns capture on for an identity that holds the permission, unless they have already turned it\n * off themselves.\n *\n * Holding `dev-tools.debug` is the opt-in: the permission is granted deliberately, for a support\n * session, and nobody should then have to find a second switch before the thing they were asked\n * to reproduce is captured.\n */\n public applyPermission = (canCapture: boolean): void => {\n if (!canCapture) {\n this._enabled = false;\n return;\n }\n\n if (readStorage(TOGGLE_STORAGE_KEY) === null) {\n this._enabled = true;\n }\n };\n\n public get enabled(): boolean {\n return this._enabled;\n }\n\n public get namespaces(): string {\n return this._namespaces;\n }\n\n public get sessions(): ICollectedSession[] {\n return this._sessions;\n }\n\n public get entryCount(): number {\n return this._sessions.reduce((total, session) => {\n return total + (session.entries?.length || 0);\n }, 0);\n }\n\n public setEnabled = (enabled: boolean): void => {\n this._enabled = enabled;\n writeStorage(TOGGLE_STORAGE_KEY, enabled ? \"true\" : \"false\");\n };\n\n public setNamespaces = (namespaces: string): void => {\n this._namespaces = namespaces;\n writeStorage(NAMESPACES_STORAGE_KEY, namespaces || null);\n };\n\n public collect = (payload: IDebugPayload): void => {\n /**\n * The server already omits sessions that captured nothing. This guards the case where a\n * response carries an envelope with no entries anyway - there is nothing to read in it, and\n * it would only pad the report.\n */\n if (!payload.entries?.length) {\n return;\n }\n\n this._sessions.push({ ...payload, collectedAt: Date.now() });\n };\n\n public clear = (): void => {\n this._sessions = [];\n };\n}\n\n/**\n * A single store instance, read by the Apollo link on every request and by the panel.\n */\nexport const debuggerStore = new DebuggerStore();\n"],"names":["TOGGLE_STORAGE_KEY","NAMESPACES_STORAGE_KEY","readStorage","key","window","writeStorage","value","DebuggerStore","canCapture","enabled","namespaces","payload","Date","makeAutoObservable","total","session","debuggerStore"],"mappings":";AAEA,MAAMA,qBAAqB;AAC3B,MAAMC,yBAAyB;AA0B/B,MAAMC,cAAc,CAACC;IACjB,IAAI;QACA,OAAOC,OAAO,YAAY,CAAC,OAAO,CAACD;IACvC,EAAE,OAAM;QACJ,OAAO;IACX;AACJ;AAEA,MAAME,eAAe,CAACF,KAAaG;IAC/B,IAAI;QACA,IAAIA,AAAU,SAAVA,OAAgB,YAChBF,OAAO,YAAY,CAAC,UAAU,CAACD;QAGnCC,OAAO,YAAY,CAAC,OAAO,CAACD,KAAKG;IACrC,EAAE,OAAM,CAER;AACJ;AASO,MAAMC;IAKT,aAAqB;aAFb,SAAS,GAAwB,EAAE;QAmB1C,KACM,eAAe,GAAG,CAACC;YACtB,IAAI,CAACA,YAAY;gBACb,IAAI,CAAC,QAAQ,GAAG;gBAChB;YACJ;YAEA,IAAIN,AAAoC,SAApCA,YAAYF,qBACZ,IAAI,CAAC,QAAQ,GAAG;QAExB;aAoBO,UAAU,GAAG,CAACS;YACjB,IAAI,CAAC,QAAQ,GAAGA;YAChBJ,aAAaL,oBAAoBS,UAAU,SAAS;QACxD;aAEO,aAAa,GAAG,CAACC;YACpB,IAAI,CAAC,WAAW,GAAGA;YACnBL,aAAaJ,wBAAwBS,cAAc;QACvD;aAEO,OAAO,GAAG,CAACC;YAMd,IAAI,CAACA,QAAQ,OAAO,EAAE,QAClB;YAGJ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;gBAAE,GAAGA,OAAO;gBAAE,aAAaC,KAAK,GAAG;YAAG;QAC9D;aAEO,KAAK,GAAG;YACX,IAAI,CAAC,SAAS,GAAG,EAAE;QACvB;QAnEI,IAAI,CAAC,QAAQ,GAAGV,AAAoC,WAApCA,YAAYF;QAC5B,IAAI,CAAC,WAAW,GAAGE,YAAYD,2BAA2B;QAC1DY,mBAAmB,IAAI;IAC3B;IAqBA,IAAW,UAAmB;QAC1B,OAAO,IAAI,CAAC,QAAQ;IACxB;IAEA,IAAW,aAAqB;QAC5B,OAAO,IAAI,CAAC,WAAW;IAC3B;IAEA,IAAW,WAAgC;QACvC,OAAO,IAAI,CAAC,SAAS;IACzB;IAEA,IAAW,aAAqB;QAC5B,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAACC,OAAOC,UAC1BD,QAASC,CAAAA,QAAQ,OAAO,EAAE,UAAU,IAC5C;IACP;AA4BJ;AAKO,MAAMC,gBAAgB,IAAIT"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import react, { useCallback } from "react";
|
|
2
|
+
import { observer } from "mobx-react-lite";
|
|
3
|
+
import { Alert } from "@webiny/admin-ui/Alert/index.js";
|
|
4
|
+
import { Button } from "@webiny/admin-ui/Button/index.js";
|
|
5
|
+
import { Switch } from "@webiny/admin-ui/Switch/index.js";
|
|
6
|
+
import { debuggerStore } from "./DebuggerStore.js";
|
|
7
|
+
import { downloadReport } from "./report.js";
|
|
8
|
+
const DebuggerView = observer(()=>{
|
|
9
|
+
const onToggle = useCallback((checked)=>{
|
|
10
|
+
debuggerStore.setEnabled(checked);
|
|
11
|
+
}, []);
|
|
12
|
+
const onNamespacesChange = useCallback((event)=>{
|
|
13
|
+
debuggerStore.setNamespaces(event.target.value);
|
|
14
|
+
}, []);
|
|
15
|
+
const hasSessions = debuggerStore.sessions.length > 0;
|
|
16
|
+
return /*#__PURE__*/ react.createElement("div", {
|
|
17
|
+
className: "flex flex-col gap-lg p-lg"
|
|
18
|
+
}, debuggerStore.enabled ? /*#__PURE__*/ react.createElement(Alert, {
|
|
19
|
+
type: "warning"
|
|
20
|
+
}, "Debug capture is on. Responses include internal diagnostic data, which may contain content from your records. Turn it off when you are done.") : null, /*#__PURE__*/ react.createElement(Switch, {
|
|
21
|
+
label: "Capture debug data",
|
|
22
|
+
checked: debuggerStore.enabled,
|
|
23
|
+
onChange: onToggle,
|
|
24
|
+
note: "Reproduce the problem with this on, then download the report and send it to support."
|
|
25
|
+
}), /*#__PURE__*/ react.createElement("label", {
|
|
26
|
+
className: "flex flex-col gap-xs"
|
|
27
|
+
}, /*#__PURE__*/ react.createElement("span", null, "Namespaces"), /*#__PURE__*/ react.createElement("input", {
|
|
28
|
+
type: "text",
|
|
29
|
+
className: "border rounded-md p-sm",
|
|
30
|
+
value: debuggerStore.namespaces,
|
|
31
|
+
onChange: onNamespacesChange
|
|
32
|
+
}), /*#__PURE__*/ react.createElement("span", {
|
|
33
|
+
className: "text-sm"
|
|
34
|
+
}, "Leave as * to capture everything, or narrow it down - for example cms.os.*")), /*#__PURE__*/ react.createElement("div", {
|
|
35
|
+
className: "flex gap-sm items-center"
|
|
36
|
+
}, /*#__PURE__*/ react.createElement("span", null, debuggerStore.sessions.length, " responses collected, ", debuggerStore.entryCount, " ", "entries"), /*#__PURE__*/ react.createElement(Button, {
|
|
37
|
+
text: "Download report",
|
|
38
|
+
onClick: downloadReport,
|
|
39
|
+
disabled: !hasSessions
|
|
40
|
+
}), /*#__PURE__*/ react.createElement(Button, {
|
|
41
|
+
variant: "secondary",
|
|
42
|
+
text: "Clear",
|
|
43
|
+
onClick: debuggerStore.clear,
|
|
44
|
+
disabled: !hasSessions
|
|
45
|
+
})));
|
|
46
|
+
});
|
|
47
|
+
export { DebuggerView };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=DebuggerView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/debugger/DebuggerView.js","sources":["../../../src/features/debugger/DebuggerView.tsx"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport { Alert } from \"@webiny/admin-ui/Alert/index.js\";\nimport { Button } from \"@webiny/admin-ui/Button/index.js\";\nimport { Switch } from \"@webiny/admin-ui/Switch/index.js\";\nimport { debuggerStore } from \"./DebuggerStore.js\";\nimport { downloadReport } from \"./report.js\";\n\nexport const DebuggerView = observer(() => {\n const onToggle = useCallback((checked: boolean) => {\n debuggerStore.setEnabled(checked);\n }, []);\n\n const onNamespacesChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n debuggerStore.setNamespaces(event.target.value);\n }, []);\n\n const hasSessions = debuggerStore.sessions.length > 0;\n\n return (\n <div className={\"flex flex-col gap-lg p-lg\"}>\n {debuggerStore.enabled ? (\n <Alert type={\"warning\"}>\n Debug capture is on. Responses include internal diagnostic data, which may\n contain content from your records. Turn it off when you are done.\n </Alert>\n ) : null}\n\n <Switch\n label={\"Capture debug data\"}\n checked={debuggerStore.enabled}\n onChange={onToggle}\n note={\n \"Reproduce the problem with this on, then download the report and send it to \" +\n \"support.\"\n }\n />\n\n <label className={\"flex flex-col gap-xs\"}>\n <span>Namespaces</span>\n <input\n type={\"text\"}\n className={\"border rounded-md p-sm\"}\n value={debuggerStore.namespaces}\n onChange={onNamespacesChange}\n />\n <span className={\"text-sm\"}>\n Leave as * to capture everything, or narrow it down - for example cms.os.*\n </span>\n </label>\n\n <div className={\"flex gap-sm items-center\"}>\n <span>\n {debuggerStore.sessions.length} responses collected, {debuggerStore.entryCount}{\" \"}\n entries\n </span>\n <Button text={\"Download report\"} onClick={downloadReport} disabled={!hasSessions} />\n <Button\n variant={\"secondary\"}\n text={\"Clear\"}\n onClick={debuggerStore.clear}\n disabled={!hasSessions}\n />\n </div>\n </div>\n );\n});\n"],"names":["DebuggerView","observer","onToggle","useCallback","checked","debuggerStore","onNamespacesChange","event","hasSessions","Alert","Switch","Button","downloadReport"],"mappings":";;;;;;;AAQO,MAAMA,eAAeC,SAAS;IACjC,MAAMC,WAAWC,YAAY,CAACC;QAC1BC,cAAc,UAAU,CAACD;IAC7B,GAAG,EAAE;IAEL,MAAME,qBAAqBH,YAAY,CAACI;QACpCF,cAAc,aAAa,CAACE,MAAM,MAAM,CAAC,KAAK;IAClD,GAAG,EAAE;IAEL,MAAMC,cAAcH,cAAc,QAAQ,CAAC,MAAM,GAAG;IAEpD,OAAO,WAAP,GACI,oBAAC;QAAI,WAAW;OACXA,cAAc,OAAO,GAAG,WAAH,GAClB,oBAACI,OAAKA;QAAC,MAAM;OAAW,kJAIxB,oBAEJ,oBAACC,QAAMA;QACH,OAAO;QACP,SAASL,cAAc,OAAO;QAC9B,UAAUH;QACV,MACI;sBAKR,oBAAC;QAAM,WAAW;qBACd,oBAAC,cAAK,6BACN,oBAAC;QACG,MAAM;QACN,WAAW;QACX,OAAOG,cAAc,UAAU;QAC/B,UAAUC;sBAEd,oBAAC;QAAK,WAAW;OAAW,8FAKhC,oBAAC;QAAI,WAAW;qBACZ,oBAAC,cACID,cAAc,QAAQ,CAAC,MAAM,EAAC,0BAAuBA,cAAc,UAAU,EAAE,KAAI,0BAGxF,oBAACM,QAAMA;QAAC,MAAM;QAAmB,SAASC;QAAgB,UAAU,CAACJ;sBACrE,oBAACG,QAAMA;QACH,SAAS;QACT,MAAM;QACN,SAASN,cAAc,KAAK;QAC5B,UAAU,CAACG;;AAK/B"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { GraphQLExtensionsEvent, GraphQLExtensionsEventHandler } from "@webiny/app/features/graphqlClient/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Collects debug data from responses that did not go through Apollo.
|
|
4
|
+
*
|
|
5
|
+
* Newer parts of Admin - Website Builder among them - use the fetch-based `GraphQLClient` rather
|
|
6
|
+
* than Apollo, and that client returns only `data`. Without this the debugger would silently miss
|
|
7
|
+
* everything they do, which looks identical to capture being broken.
|
|
8
|
+
*/
|
|
9
|
+
declare class GraphQLExtensionsDebugHandlerImpl implements GraphQLExtensionsEventHandler.Interface {
|
|
10
|
+
handle(event: GraphQLExtensionsEvent): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export declare const GraphQLExtensionsDebugHandler: typeof GraphQLExtensionsDebugHandlerImpl & {
|
|
13
|
+
__abstraction: import("@webiny/di").Abstraction<import("@webiny/app/features/eventPublisher/abstractions.js").IEventHandler<GraphQLExtensionsEvent>>;
|
|
14
|
+
};
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { GraphQLExtensionsEventHandler } from "@webiny/app/features/graphqlClient/index.js";
|
|
2
|
+
import { debuggerStore } from "./DebuggerStore.js";
|
|
3
|
+
class GraphQLExtensionsDebugHandlerImpl {
|
|
4
|
+
async handle(event) {
|
|
5
|
+
const debug = event.payload.extensions.debug;
|
|
6
|
+
if (debug) debuggerStore.collect(debug);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
const GraphQLExtensionsDebugHandler = GraphQLExtensionsEventHandler.createImplementation({
|
|
10
|
+
implementation: GraphQLExtensionsDebugHandlerImpl,
|
|
11
|
+
dependencies: []
|
|
12
|
+
});
|
|
13
|
+
export { GraphQLExtensionsDebugHandler };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=GraphQLExtensionsDebugHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/debugger/GraphQLExtensionsDebugHandler.js","sources":["../../../src/features/debugger/GraphQLExtensionsDebugHandler.ts"],"sourcesContent":["import {\n GraphQLExtensionsEvent,\n GraphQLExtensionsEventHandler\n} from \"@webiny/app/features/graphqlClient/index.js\";\nimport { debuggerStore, type IDebugPayload } from \"./DebuggerStore.js\";\n\n/**\n * Collects debug data from responses that did not go through Apollo.\n *\n * Newer parts of Admin - Website Builder among them - use the fetch-based `GraphQLClient` rather\n * than Apollo, and that client returns only `data`. Without this the debugger would silently miss\n * everything they do, which looks identical to capture being broken.\n */\nclass GraphQLExtensionsDebugHandlerImpl implements GraphQLExtensionsEventHandler.Interface {\n async handle(event: GraphQLExtensionsEvent): Promise<void> {\n const debug = event.payload.extensions.debug as IDebugPayload | undefined;\n\n if (debug) {\n debuggerStore.collect(debug);\n }\n }\n}\n\nexport const GraphQLExtensionsDebugHandler = GraphQLExtensionsEventHandler.createImplementation({\n implementation: GraphQLExtensionsDebugHandlerImpl,\n dependencies: []\n});\n"],"names":["GraphQLExtensionsDebugHandlerImpl","event","debug","debuggerStore","GraphQLExtensionsDebugHandler","GraphQLExtensionsEventHandler"],"mappings":";;AAaA,MAAMA;IACF,MAAM,OAAOC,KAA6B,EAAiB;QACvD,MAAMC,QAAQD,MAAM,OAAO,CAAC,UAAU,CAAC,KAAK;QAE5C,IAAIC,OACAC,cAAc,OAAO,CAACD;IAE9B;AACJ;AAEO,MAAME,gCAAgCC,8BAA8B,oBAAoB,CAAC;IAC5F,gBAAgBL;IAChB,cAAc,EAAE;AACpB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ApolloLink } from "apollo-link";
|
|
2
|
+
import type { DebuggerStore } from "./DebuggerStore.js";
|
|
3
|
+
/**
|
|
4
|
+
* Collects whatever debug data comes back.
|
|
5
|
+
*
|
|
6
|
+
* Deliberately sends no request header. Whether to capture is decided entirely by the API from the
|
|
7
|
+
* identity's permissions, which keeps this off the CORS and CloudFront header whitelists - a header
|
|
8
|
+
* added there is cached in preflight responses for a day, so rolling one out breaks requests
|
|
9
|
+
* intermittently until every cache entry expires.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEBUG_HEADER = "x-webiny-debug";
|
|
12
|
+
export declare const createDebuggerLink: (store: DebuggerStore) => ApolloLink;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ApolloLink } from "apollo-link";
|
|
2
|
+
const DEBUG_HEADER = "x-webiny-debug";
|
|
3
|
+
const createDebuggerLink = (store)=>new ApolloLink((operation, forward)=>forward(operation).map((response)=>{
|
|
4
|
+
const debug = response.extensions?.debug;
|
|
5
|
+
if (debug) store.collect(debug);
|
|
6
|
+
return response;
|
|
7
|
+
}));
|
|
8
|
+
export { DEBUG_HEADER, createDebuggerLink };
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=debuggerLink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/debugger/debuggerLink.js","sources":["../../../src/features/debugger/debuggerLink.ts"],"sourcesContent":["import { ApolloLink } from \"apollo-link\";\nimport type { NextLink, Operation } from \"apollo-link\";\nimport type { DebuggerStore, IDebugPayload } from \"./DebuggerStore.js\";\n\n/**\n * Collects whatever debug data comes back.\n *\n * Deliberately sends no request header. Whether to capture is decided entirely by the API from the\n * identity's permissions, which keeps this off the CORS and CloudFront header whitelists - a header\n * added there is cached in preflight responses for a day, so rolling one out breaks requests\n * intermittently until every cache entry expires.\n */\nexport const DEBUG_HEADER = \"x-webiny-debug\";\n\nexport const createDebuggerLink = (store: DebuggerStore): ApolloLink => {\n return new ApolloLink((operation: Operation, forward: NextLink) => {\n /**\n * Kept for reference. Sending the header requires it on the CORS allow-list, which is\n * cached in preflight responses at CloudFront and in the browser for a day - so adding it\n * breaks requests intermittently until every cache entry expires. The API decides from the\n * identity's permissions instead, and needs nothing from the client.\n *\n * if (store.enabled) {\n * operation.setContext(({ headers = {} }: { headers?: Record<string, string> }) => {\n * return {\n * headers: {\n * ...headers,\n * [DEBUG_HEADER]: store.namespaces || \"*\"\n * }\n * };\n * });\n * }\n */\n return forward(operation).map(response => {\n /**\n * Batched requests carry the payload on one arbitrary operation's result, so every\n * response is inspected rather than only the one that was asked for.\n */\n const debug = (response.extensions as { debug?: IDebugPayload } | undefined)?.debug;\n\n if (debug) {\n store.collect(debug);\n }\n\n return response;\n });\n });\n};\n"],"names":["DEBUG_HEADER","createDebuggerLink","store","ApolloLink","operation","forward","response","debug"],"mappings":";AAYO,MAAMA,eAAe;AAErB,MAAMC,qBAAqB,CAACC,QACxB,IAAIC,WAAW,CAACC,WAAsBC,UAkBlCA,QAAQD,WAAW,GAAG,CAACE,CAAAA;YAK1B,MAAMC,QAASD,SAAS,UAAU,EAA4C;YAE9E,IAAIC,OACAL,MAAM,OAAO,CAACK;YAGlB,OAAOD;QACX"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const DebuggerFeature: import("@webiny/feature/admin").FeatureDefinition<any, []>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ApolloLinkPlugin } from "@webiny/app";
|
|
2
|
+
import { plugins } from "@webiny/plugins";
|
|
3
|
+
import { createFeature } from "@webiny/feature/admin";
|
|
4
|
+
import { createDebuggerLink } from "./debuggerLink.js";
|
|
5
|
+
import { debuggerStore } from "./DebuggerStore.js";
|
|
6
|
+
import { DebuggerPermissionsFeature } from "./permissions.js";
|
|
7
|
+
import { GraphQLExtensionsDebugHandler } from "./GraphQLExtensionsDebugHandler.js";
|
|
8
|
+
const DebuggerFeature = createFeature({
|
|
9
|
+
name: "Debugger",
|
|
10
|
+
register (container) {
|
|
11
|
+
DebuggerPermissionsFeature.register(container);
|
|
12
|
+
container.register(GraphQLExtensionsDebugHandler).inSingletonScope();
|
|
13
|
+
plugins.register(new ApolloLinkPlugin(()=>createDebuggerLink(debuggerStore)));
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
export { DebuggerFeature };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=feature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/debugger/feature.js","sources":["../../../src/features/debugger/feature.ts"],"sourcesContent":["import { ApolloLinkPlugin } from \"@webiny/app\";\nimport { plugins } from \"@webiny/plugins\";\nimport { createFeature } from \"@webiny/feature/admin\";\nimport { createDebuggerLink } from \"./debuggerLink.js\";\nimport { debuggerStore } from \"./DebuggerStore.js\";\nimport { DebuggerPermissionsFeature } from \"./permissions.js\";\nimport { GraphQLExtensionsDebugHandler } from \"./GraphQLExtensionsDebugHandler.js\";\n\nexport const DebuggerFeature = createFeature({\n name: \"Debugger\",\n register(container) {\n DebuggerPermissionsFeature.register(container);\n\n /**\n * Collects from the fetch-based client, which Apollo links never see.\n */\n container.register(GraphQLExtensionsDebugHandler).inSingletonScope();\n\n /**\n * The link is always registered; whether it does anything is decided per request from the\n * toggle. Registering conditionally would mean the link never appears for a toggle flipped\n * after start-up.\n */\n plugins.register(new ApolloLinkPlugin(() => createDebuggerLink(debuggerStore)));\n }\n});\n"],"names":["DebuggerFeature","createFeature","container","DebuggerPermissionsFeature","GraphQLExtensionsDebugHandler","plugins","ApolloLinkPlugin","createDebuggerLink","debuggerStore"],"mappings":";;;;;;;AAQO,MAAMA,kBAAkBC,cAAc;IACzC,MAAM;IACN,UAASC,SAAS;QACdC,2BAA2B,QAAQ,CAACD;QAKpCA,UAAU,QAAQ,CAACE,+BAA+B,gBAAgB;QAOlEC,QAAQ,QAAQ,CAAC,IAAIC,iBAAiB,IAAMC,mBAAmBC;IACnE;AACJ"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { Debugger } from "./Debugger.js";
|
|
2
|
+
export { DebuggerIndicator } from "./DebuggerIndicator.js";
|
|
3
|
+
export { DebuggerFeature } from "./feature.js";
|
|
4
|
+
export { debuggerStore, DebuggerStore } from "./DebuggerStore.js";
|
|
5
|
+
export { DebuggerRoutes } from "./routes.js";
|
|
6
|
+
export { DEBUGGER_PERMISSIONS_SCHEMA, DebuggerPermissions, useDebuggerPermissions } from "./permissions.js";
|
|
7
|
+
export { downloadReport } from "./report.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { Debugger } from "./Debugger.js";
|
|
2
|
+
export { DebuggerIndicator } from "./DebuggerIndicator.js";
|
|
3
|
+
export { DebuggerFeature } from "./feature.js";
|
|
4
|
+
export { DebuggerStore, debuggerStore } from "./DebuggerStore.js";
|
|
5
|
+
export { DebuggerRoutes } from "./routes.js";
|
|
6
|
+
export { DEBUGGER_PERMISSIONS_SCHEMA, DebuggerPermissions, useDebuggerPermissions } from "./permissions.js";
|
|
7
|
+
export { downloadReport } from "./report.js";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registered under its own name rather than joining the `dev-tools` group declared by the
|
|
3
|
+
* playgrounds: `Security.Permissions` is keyed by name, so a second registration using the same name
|
|
4
|
+
* would replace theirs and hide their entities from the role editor.
|
|
5
|
+
*
|
|
6
|
+
* The permission itself is `dev-tools.debug`, so it still sits under the Dev Tools prefix - and note
|
|
7
|
+
* the consequence: `dev-tools.*` held by a Dev Tools full-access role matches it.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEBUGGER_PERMISSIONS_SCHEMA: {
|
|
10
|
+
readonly prefix: "dev-tools";
|
|
11
|
+
readonly fullAccess: true;
|
|
12
|
+
readonly entities: [{
|
|
13
|
+
readonly id: "debug";
|
|
14
|
+
readonly title: "Debugger";
|
|
15
|
+
readonly permission: "dev-tools.debug";
|
|
16
|
+
readonly scopes: ["full"];
|
|
17
|
+
}];
|
|
18
|
+
};
|
|
19
|
+
export declare const DebuggerPermissions: import("@webiny/di").Abstraction<{
|
|
20
|
+
canAccess: (entityId: "debug") => boolean;
|
|
21
|
+
canAction: (action: string, entityId: "debug") => boolean;
|
|
22
|
+
} & {
|
|
23
|
+
canRead: (entityId: string) => boolean;
|
|
24
|
+
canCreate: (entityId: string) => boolean;
|
|
25
|
+
canEdit: (entityId: string, item?: import("~/permissions/index.js").OwnableItem) => boolean;
|
|
26
|
+
canDelete: (entityId: string, item?: import("~/permissions/index.js").OwnableItem) => boolean;
|
|
27
|
+
} & {
|
|
28
|
+
canPublish: (entityId: string) => boolean;
|
|
29
|
+
canUnpublish: (entityId: string) => boolean;
|
|
30
|
+
}>;
|
|
31
|
+
export declare const DebuggerPermissionsFeature: import("@webiny/feature/admin").FeatureDefinition<{
|
|
32
|
+
permissions: {
|
|
33
|
+
canAccess: (entityId: "debug") => boolean;
|
|
34
|
+
canAction: (action: string, entityId: "debug") => boolean;
|
|
35
|
+
} & {
|
|
36
|
+
canRead: (entityId: string) => boolean;
|
|
37
|
+
canCreate: (entityId: string) => boolean;
|
|
38
|
+
canEdit: (entityId: string, item?: import("~/permissions/index.js").OwnableItem) => boolean;
|
|
39
|
+
canDelete: (entityId: string, item?: import("~/permissions/index.js").OwnableItem) => boolean;
|
|
40
|
+
} & {
|
|
41
|
+
canPublish: (entityId: string) => boolean;
|
|
42
|
+
canUnpublish: (entityId: string) => boolean;
|
|
43
|
+
};
|
|
44
|
+
}, []>;
|
|
45
|
+
export declare const useDebuggerPermissions: () => {
|
|
46
|
+
canAccess: (entityId: "debug") => boolean;
|
|
47
|
+
canAction: (action: string, entityId: "debug") => boolean;
|
|
48
|
+
} & {
|
|
49
|
+
canRead: (entityId: string) => boolean;
|
|
50
|
+
canCreate: (entityId: string) => boolean;
|
|
51
|
+
canEdit: (entityId: string, item?: import("~/permissions/index.js").OwnableItem) => boolean;
|
|
52
|
+
canDelete: (entityId: string, item?: import("~/permissions/index.js").OwnableItem) => boolean;
|
|
53
|
+
} & {
|
|
54
|
+
canPublish: (entityId: string) => boolean;
|
|
55
|
+
canUnpublish: (entityId: string) => boolean;
|
|
56
|
+
};
|
|
57
|
+
export declare const DEBUG_PERMISSION_NAME = "dev-tools.debug";
|
|
58
|
+
/**
|
|
59
|
+
* Whether the current identity may capture debug data.
|
|
60
|
+
*
|
|
61
|
+
* An exact match, deliberately: `*` and `dev-tools.*` do not grant it. Capture runs automatically
|
|
62
|
+
* for whoever holds this, so a wildcard would leave it permanently on for every full-access
|
|
63
|
+
* administrator, collecting record content on every request for people who never asked for it.
|
|
64
|
+
*
|
|
65
|
+
* Full access is therefore not enough. The entity has to be granted deliberately, and that act is
|
|
66
|
+
* the opt-in.
|
|
67
|
+
*/
|
|
68
|
+
export declare const useCanCaptureDebugData: () => boolean;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useAuthentication } from "../../presentation/security/hooks/useAuthentication.js";
|
|
2
|
+
import { createPermissionSchema, createPermissionsAbstraction, createPermissionsFeature, createUsePermissions } from "../../permissions/index.js";
|
|
3
|
+
const DEBUGGER_PERMISSIONS_SCHEMA = createPermissionSchema({
|
|
4
|
+
prefix: "dev-tools",
|
|
5
|
+
fullAccess: true,
|
|
6
|
+
entities: [
|
|
7
|
+
{
|
|
8
|
+
id: "debug",
|
|
9
|
+
title: "Debugger",
|
|
10
|
+
permission: "dev-tools.debug",
|
|
11
|
+
scopes: [
|
|
12
|
+
"full"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
});
|
|
17
|
+
const DebuggerPermissions = createPermissionsAbstraction(DEBUGGER_PERMISSIONS_SCHEMA);
|
|
18
|
+
const DebuggerPermissionsFeature = createPermissionsFeature(DEBUGGER_PERMISSIONS_SCHEMA, DebuggerPermissions);
|
|
19
|
+
const useDebuggerPermissions = createUsePermissions(DebuggerPermissions);
|
|
20
|
+
const DEBUG_PERMISSION_NAME = "dev-tools.debug";
|
|
21
|
+
const useCanCaptureDebugData = ()=>{
|
|
22
|
+
const { identity } = useAuthentication();
|
|
23
|
+
return Boolean(identity.getPermission(DEBUG_PERMISSION_NAME, true));
|
|
24
|
+
};
|
|
25
|
+
export { DEBUGGER_PERMISSIONS_SCHEMA, DEBUG_PERMISSION_NAME, DebuggerPermissions, DebuggerPermissionsFeature, useCanCaptureDebugData, useDebuggerPermissions };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=permissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/debugger/permissions.js","sources":["../../../src/features/debugger/permissions.ts"],"sourcesContent":["import { useAuthentication } from \"~/presentation/security/hooks/useAuthentication.js\";\nimport {\n createPermissionSchema,\n createPermissionsAbstraction,\n createPermissionsFeature,\n createUsePermissions\n} from \"~/permissions/index.js\";\n\n/**\n * Registered under its own name rather than joining the `dev-tools` group declared by the\n * playgrounds: `Security.Permissions` is keyed by name, so a second registration using the same name\n * would replace theirs and hide their entities from the role editor.\n *\n * The permission itself is `dev-tools.debug`, so it still sits under the Dev Tools prefix - and note\n * the consequence: `dev-tools.*` held by a Dev Tools full-access role matches it.\n */\nexport const DEBUGGER_PERMISSIONS_SCHEMA = createPermissionSchema({\n prefix: \"dev-tools\",\n fullAccess: true,\n entities: [\n {\n id: \"debug\",\n title: \"Debugger\",\n permission: \"dev-tools.debug\",\n scopes: [\"full\"]\n }\n ]\n});\n\nexport const DebuggerPermissions = createPermissionsAbstraction(DEBUGGER_PERMISSIONS_SCHEMA);\n\nexport const DebuggerPermissionsFeature = createPermissionsFeature(\n DEBUGGER_PERMISSIONS_SCHEMA,\n DebuggerPermissions\n);\n\nexport const useDebuggerPermissions = createUsePermissions(DebuggerPermissions);\n\nexport const DEBUG_PERMISSION_NAME = \"dev-tools.debug\";\n\n/**\n * Whether the current identity may capture debug data.\n *\n * An exact match, deliberately: `*` and `dev-tools.*` do not grant it. Capture runs automatically\n * for whoever holds this, so a wildcard would leave it permanently on for every full-access\n * administrator, collecting record content on every request for people who never asked for it.\n *\n * Full access is therefore not enough. The entity has to be granted deliberately, and that act is\n * the opt-in.\n */\nexport const useCanCaptureDebugData = (): boolean => {\n const { identity } = useAuthentication();\n return Boolean(identity.getPermission(DEBUG_PERMISSION_NAME, true));\n};\n"],"names":["DEBUGGER_PERMISSIONS_SCHEMA","createPermissionSchema","DebuggerPermissions","createPermissionsAbstraction","DebuggerPermissionsFeature","createPermissionsFeature","useDebuggerPermissions","createUsePermissions","DEBUG_PERMISSION_NAME","useCanCaptureDebugData","identity","useAuthentication","Boolean"],"mappings":";;AAgBO,MAAMA,8BAA8BC,uBAAuB;IAC9D,QAAQ;IACR,YAAY;IACZ,UAAU;QACN;YACI,IAAI;YACJ,OAAO;YACP,YAAY;YACZ,QAAQ;gBAAC;aAAO;QACpB;KACH;AACL;AAEO,MAAMC,sBAAsBC,6BAA6BH;AAEzD,MAAMI,6BAA6BC,yBACtCL,6BACAE;AAGG,MAAMI,yBAAyBC,qBAAqBL;AAEpD,MAAMM,wBAAwB;AAY9B,MAAMC,yBAAyB;IAClC,MAAM,EAAEC,QAAQ,EAAE,GAAGC;IACrB,OAAOC,QAAQF,SAAS,aAAa,CAACF,uBAAuB;AACjE"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hands the collected sessions to the user as a file.
|
|
3
|
+
*
|
|
4
|
+
* Nothing is uploaded anywhere. The report leaves the browser only when the person chooses to send
|
|
5
|
+
* it, which is the moment they decide this data may leave their organisation.
|
|
6
|
+
*/
|
|
7
|
+
export declare const downloadReport: () => void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { debuggerStore } from "./DebuggerStore.js";
|
|
2
|
+
const buildReport = ()=>({
|
|
3
|
+
generatedAt: new Date().toISOString(),
|
|
4
|
+
namespaces: debuggerStore.namespaces,
|
|
5
|
+
sessions: debuggerStore.sessions
|
|
6
|
+
});
|
|
7
|
+
const downloadReport = ()=>{
|
|
8
|
+
const blob = new Blob([
|
|
9
|
+
JSON.stringify(buildReport(), null, 2)
|
|
10
|
+
], {
|
|
11
|
+
type: "application/json"
|
|
12
|
+
});
|
|
13
|
+
const url = URL.createObjectURL(blob);
|
|
14
|
+
const link = document.createElement("a");
|
|
15
|
+
link.href = url;
|
|
16
|
+
link.download = `webiny-debug-${new Date().toISOString()}.json`;
|
|
17
|
+
link.click();
|
|
18
|
+
URL.revokeObjectURL(url);
|
|
19
|
+
};
|
|
20
|
+
export { downloadReport };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/debugger/report.js","sources":["../../../src/features/debugger/report.ts"],"sourcesContent":["import { debuggerStore } from \"./DebuggerStore.js\";\n\nconst buildReport = () => {\n return {\n generatedAt: new Date().toISOString(),\n namespaces: debuggerStore.namespaces,\n sessions: debuggerStore.sessions\n };\n};\n\n/**\n * Hands the collected sessions to the user as a file.\n *\n * Nothing is uploaded anywhere. The report leaves the browser only when the person chooses to send\n * it, which is the moment they decide this data may leave their organisation.\n */\nexport const downloadReport = () => {\n const blob = new Blob([JSON.stringify(buildReport(), null, 2)], {\n type: \"application/json\"\n });\n const url = URL.createObjectURL(blob);\n\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = `webiny-debug-${new Date().toISOString()}.json`;\n link.click();\n\n URL.revokeObjectURL(url);\n};\n"],"names":["buildReport","Date","debuggerStore","downloadReport","blob","Blob","JSON","url","URL","link","document"],"mappings":";AAEA,MAAMA,cAAc,IACT;QACH,aAAa,IAAIC,OAAO,WAAW;QACnC,YAAYC,cAAc,UAAU;QACpC,UAAUA,cAAc,QAAQ;IACpC;AASG,MAAMC,iBAAiB;IAC1B,MAAMC,OAAO,IAAIC,KAAK;QAACC,KAAK,SAAS,CAACN,eAAe,MAAM;KAAG,EAAE;QAC5D,MAAM;IACV;IACA,MAAMO,MAAMC,IAAI,eAAe,CAACJ;IAEhC,MAAMK,OAAOC,SAAS,aAAa,CAAC;IACpCD,KAAK,IAAI,GAAGF;IACZE,KAAK,QAAQ,GAAG,CAAC,aAAa,EAAE,IAAIR,OAAO,WAAW,GAAG,KAAK,CAAC;IAC/DQ,KAAK,KAAK;IAEVD,IAAI,eAAe,CAACD;AACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/debugger/routes.js","sources":["../../../src/features/debugger/routes.ts"],"sourcesContent":["import { Route } from \"@webiny/app\";\n\nexport const DebuggerRoutes = {\n Debugger: new Route({\n name: \"Debugger\",\n path: \"/debugger\"\n })\n};\n"],"names":["DebuggerRoutes","Route"],"mappings":";AAEO,MAAMA,iBAAiB;IAC1B,UAAU,IAAIC,MAAM;QAChB,MAAM;QACN,MAAM;IACV;AACJ"}
|
|
@@ -3,6 +3,7 @@ import { observer } from "mobx-react-lite";
|
|
|
3
3
|
import { makeDecoratable } from "@webiny/react-composition";
|
|
4
4
|
const FormFieldWrapper = makeDecoratable("FormFieldWrapper", ({ children })=>/*#__PURE__*/ react.createElement(react.Fragment, null, children));
|
|
5
5
|
const FOCUS_HIGHLIGHT_MS = 1800;
|
|
6
|
+
const FOCUS_HIGHLIGHT_SHADOW = "0 0 0 6px var(--color-primary-100), 0 0 0 8px var(--color-primary)";
|
|
6
7
|
const ScrollOnFocus = observer(({ field, children })=>{
|
|
7
8
|
const ref = useRef(null);
|
|
8
9
|
const [highlighted, setHighlighted] = useState(false);
|
|
@@ -30,11 +31,9 @@ const ScrollOnFocus = observer(({ field, children })=>{
|
|
|
30
31
|
ref: ref,
|
|
31
32
|
"data-field-path": field.qualifiedName,
|
|
32
33
|
style: {
|
|
33
|
-
|
|
34
|
-
margin: -6,
|
|
35
|
-
borderRadius: 8,
|
|
34
|
+
borderRadius: 2,
|
|
36
35
|
transition: "box-shadow .35s ease, background-color .35s ease",
|
|
37
|
-
boxShadow: highlighted ?
|
|
36
|
+
boxShadow: highlighted ? FOCUS_HIGHLIGHT_SHADOW : void 0,
|
|
38
37
|
backgroundColor: highlighted ? "var(--color-primary-100)" : void 0
|
|
39
38
|
}
|
|
40
39
|
}, /*#__PURE__*/ react.createElement(FormFieldWrapper, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/formModel/createFieldRenderer.js","sources":["../../../src/features/formModel/createFieldRenderer.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport { makeDecoratable } from \"@webiny/react-composition\";\nimport type { IFieldVM, IObjectFieldVM, FieldRendererSettings } from \"./abstractions.js\";\n\n/**\n * Decoratable wrapper rendered around every leaf field's content. Features can decorate it to\n * inject per-field UI (e.g. a comment marker) keyed on `field.qualifiedName`, without forking\n * the form. By default it renders the field content unchanged.\n */\nexport const FormFieldWrapper = makeDecoratable(\n \"FormFieldWrapper\",\n ({ children }: { field: IFieldVM; children?: React.ReactNode }) => {\n return <>{children}</>;\n }\n);\n\ntype RendererField<TName extends string> = IFieldVM & {\n rendererSettings: FieldRendererSettings<TName>;\n};\n\ntype ObjectRendererField<TName extends string> = IObjectFieldVM & {\n rendererSettings: FieldRendererSettings<TName>;\n};\n\ntype FieldRendererFn<TName extends string> = (props: {\n field: RendererField<TName>;\n}) => React.ReactNode;\n\n// How long the field stays visually highlighted after focus lands, so the user's eye is drawn\n// to where \"jump to field\" (a comment locator, a form error) scrolled to.\nconst FOCUS_HIGHLIGHT_MS = 1800;\n\nconst ScrollOnFocus = observer(\n ({ field, children }: { field: IFieldVM; children: React.ReactNode }) => {\n const ref = useRef<HTMLDivElement>(null);\n const [highlighted, setHighlighted] = useState(false);\n const highlightTimer = useRef<number | null>(null);\n\n useEffect(() => {\n if (field.focusRequested && ref.current) {\n setTimeout(() => {\n ref.current?.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n }, 150);\n // Flash a highlight ring so it's obvious where focus landed. The timer is kept in a\n // ref (not tied to the effect cleanup) so clearing the focus request below — which\n // re-runs this effect — doesn't cut the highlight short.\n setHighlighted(true);\n if (highlightTimer.current) {\n window.clearTimeout(highlightTimer.current);\n }\n highlightTimer.current = window.setTimeout(\n () => setHighlighted(false),\n FOCUS_HIGHLIGHT_MS\n );\n }\n field.clearFocusRequest();\n }, [field.focusRequested]);\n\n useEffect(() => {\n return () => {\n if (highlightTimer.current) {\n window.clearTimeout(highlightTimer.current);\n }\n };\n }, []);\n\n return (\n <div\n ref={ref}\n data-field-path={field.qualifiedName}\n style={{\n
|
|
1
|
+
{"version":3,"file":"features/formModel/createFieldRenderer.js","sources":["../../../src/features/formModel/createFieldRenderer.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport { makeDecoratable } from \"@webiny/react-composition\";\nimport type { IFieldVM, IObjectFieldVM, FieldRendererSettings } from \"./abstractions.js\";\n\n/**\n * Decoratable wrapper rendered around every leaf field's content. Features can decorate it to\n * inject per-field UI (e.g. a comment marker) keyed on `field.qualifiedName`, without forking\n * the form. By default it renders the field content unchanged.\n */\nexport const FormFieldWrapper = makeDecoratable(\n \"FormFieldWrapper\",\n ({ children }: { field: IFieldVM; children?: React.ReactNode }) => {\n return <>{children}</>;\n }\n);\n\ntype RendererField<TName extends string> = IFieldVM & {\n rendererSettings: FieldRendererSettings<TName>;\n};\n\ntype ObjectRendererField<TName extends string> = IObjectFieldVM & {\n rendererSettings: FieldRendererSettings<TName>;\n};\n\ntype FieldRendererFn<TName extends string> = (props: {\n field: RendererField<TName>;\n}) => React.ReactNode;\n\n// How long the field stays visually highlighted after focus lands, so the user's eye is drawn\n// to where \"jump to field\" (a comment locator, a form error) scrolled to.\nconst FOCUS_HIGHLIGHT_MS = 1800;\n\n// A 6px tint around the field, then a 2px ring outside it.\nconst FOCUS_HIGHLIGHT_SHADOW = \"0 0 0 6px var(--color-primary-100), 0 0 0 8px var(--color-primary)\";\n\nconst ScrollOnFocus = observer(\n ({ field, children }: { field: IFieldVM; children: React.ReactNode }) => {\n const ref = useRef<HTMLDivElement>(null);\n const [highlighted, setHighlighted] = useState(false);\n const highlightTimer = useRef<number | null>(null);\n\n useEffect(() => {\n if (field.focusRequested && ref.current) {\n setTimeout(() => {\n ref.current?.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n }, 150);\n // Flash a highlight ring so it's obvious where focus landed. The timer is kept in a\n // ref (not tied to the effect cleanup) so clearing the focus request below — which\n // re-runs this effect — doesn't cut the highlight short.\n setHighlighted(true);\n if (highlightTimer.current) {\n window.clearTimeout(highlightTimer.current);\n }\n highlightTimer.current = window.setTimeout(\n () => setHighlighted(false),\n FOCUS_HIGHLIGHT_MS\n );\n }\n field.clearFocusRequest();\n }, [field.focusRequested]);\n\n useEffect(() => {\n return () => {\n if (highlightTimer.current) {\n window.clearTimeout(highlightTimer.current);\n }\n };\n }, []);\n\n return (\n <div\n ref={ref}\n data-field-path={field.qualifiedName}\n style={{\n /*\n * The highlight is drawn with shadows, which paint around the field without\n * taking up room. Padding with a matching negative margin looked the same, but\n * the padded box still stuck out of the field, so a scroll area around a form\n * always had 6px to scroll.\n */\n borderRadius: 2,\n transition: \"box-shadow .35s ease, background-color .35s ease\",\n boxShadow: highlighted ? FOCUS_HIGHLIGHT_SHADOW : undefined,\n backgroundColor: highlighted ? \"var(--color-primary-100)\" : undefined\n }}\n >\n <FormFieldWrapper field={field}>{children}</FormFieldWrapper>\n </div>\n );\n }\n);\n\nexport function createFieldRenderer<TName extends string = string>(\n render: FieldRendererFn<TName>\n): React.ComponentType<{ field: IFieldVM }> {\n const Inner = observer(render as FieldRendererFn<string>) as unknown as React.ComponentType<{\n field: IFieldVM;\n }>;\n\n const Wrapper = observer(({ field }: { field: IFieldVM }) => (\n <ScrollOnFocus field={field}>\n <Inner field={field} />\n </ScrollOnFocus>\n ));\n\n return Wrapper as unknown as React.ComponentType<{ field: IFieldVM }>;\n}\n\nexport function createObjectFieldRenderer<TName extends string = string>(\n render: (props: { field: ObjectRendererField<TName> }) => React.ReactNode\n): React.ComponentType<{ field: IFieldVM }> {\n const Wrapped = (props: { field: IFieldVM }) => {\n if (props.field.type !== \"object\") {\n return null;\n }\n return (render as (p: { field: IFieldVM }) => React.ReactNode)(props);\n };\n return observer(Wrapped) as unknown as React.ComponentType<{ field: IFieldVM }>;\n}\n"],"names":["FormFieldWrapper","makeDecoratable","children","FOCUS_HIGHLIGHT_MS","FOCUS_HIGHLIGHT_SHADOW","ScrollOnFocus","observer","field","ref","useRef","highlighted","setHighlighted","useState","highlightTimer","useEffect","setTimeout","window","undefined","createFieldRenderer","render","Inner","Wrapper","createObjectFieldRenderer","Wrapped","props"],"mappings":";;;AAUO,MAAMA,mBAAmBC,gBAC5B,oBACA,CAAC,EAAEC,QAAQ,EAAmD,GACnD,WAAP,GAAO,0CAAGA;AAkBlB,MAAMC,qBAAqB;AAG3B,MAAMC,yBAAyB;AAE/B,MAAMC,gBAAgBC,SAClB,CAAC,EAAEC,KAAK,EAAEL,QAAQ,EAAkD;IAChE,MAAMM,MAAMC,OAAuB;IACnC,MAAM,CAACC,aAAaC,eAAe,GAAGC,SAAS;IAC/C,MAAMC,iBAAiBJ,OAAsB;IAE7CK,UAAU;QACN,IAAIP,MAAM,cAAc,IAAIC,IAAI,OAAO,EAAE;YACrCO,WAAW;gBACPP,IAAI,OAAO,EAAE,eAAe;oBAAE,UAAU;oBAAU,OAAO;gBAAS;YACtE,GAAG;YAIHG,eAAe;YACf,IAAIE,eAAe,OAAO,EACtBG,OAAO,YAAY,CAACH,eAAe,OAAO;YAE9CA,eAAe,OAAO,GAAGG,OAAO,UAAU,CACtC,IAAML,eAAe,QACrBR;QAER;QACAI,MAAM,iBAAiB;IAC3B,GAAG;QAACA,MAAM,cAAc;KAAC;IAEzBO,UAAU,IACC;YACH,IAAID,eAAe,OAAO,EACtBG,OAAO,YAAY,CAACH,eAAe,OAAO;QAElD,GACD,EAAE;IAEL,OAAO,WAAP,GACI,oBAAC;QACG,KAAKL;QACL,mBAAiBD,MAAM,aAAa;QACpC,OAAO;YAOH,cAAc;YACd,YAAY;YACZ,WAAWG,cAAcN,yBAAyBa;YAClD,iBAAiBP,cAAc,6BAA6BO;QAChE;qBAEA,oBAACjB,kBAAgBA;QAAC,OAAOO;OAAQL;AAG7C;AAGG,SAASgB,oBACZC,MAA8B;IAE9B,MAAMC,QAAQd,SAASa;IAIvB,MAAME,UAAUf,SAAS,CAAC,EAAEC,KAAK,EAAuB,iBACpD,oBAACF,eAAaA;YAAC,OAAOE;yBAClB,oBAACa,OAAAA;YAAM,OAAOb;;IAItB,OAAOc;AACX;AAEO,SAASC,0BACZH,MAAyE;IAEzE,MAAMI,UAAU,CAACC;QACb,IAAIA,AAAqB,aAArBA,MAAM,KAAK,CAAC,IAAI,EAChB,OAAO;QAEX,OAAQL,OAAuDK;IACnE;IACA,OAAOlB,SAASiB;AACpB"}
|
package/hooks/useHotkeys.js
CHANGED
|
@@ -44,6 +44,8 @@ function useHotkeys(props) {
|
|
|
44
44
|
const { disabled, zIndex, keys } = props;
|
|
45
45
|
const prevPropsRef = useRef();
|
|
46
46
|
const firstRenderRef = useRef(true);
|
|
47
|
+
const propsRef = useRef(props);
|
|
48
|
+
propsRef.current = props;
|
|
47
49
|
useEffect(function() {
|
|
48
50
|
if (firstRenderRef.current || prevPropsRef.current?.disabled !== disabled) {
|
|
49
51
|
firstRenderRef.current = false;
|
|
@@ -59,7 +61,7 @@ function useHotkeys(props) {
|
|
|
59
61
|
]);
|
|
60
62
|
useEffect(function() {
|
|
61
63
|
return function() {
|
|
62
|
-
if (!disabled) unregisterZIndex(
|
|
64
|
+
if (!propsRef.current.disabled) unregisterZIndex(propsRef.current);
|
|
63
65
|
};
|
|
64
66
|
}, []);
|
|
65
67
|
}
|
package/hooks/useHotkeys.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks/useHotkeys.js","sources":["../../src/hooks/useHotkeys.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport { isHotkey } from \"is-hotkey\";\n\ntype HookProps = {\n disabled?: boolean;\n zIndex: number;\n keys?: { [key: string]: (e: KeyboardEvent) => void };\n};\n\ntype State = {\n listenerAttached: boolean;\n zIndex: null | number;\n handlers: { [zIndex: number]: { [key: string]: (e: KeyboardEvent) => void } };\n};\n\nconst state: State = {\n listenerAttached: false,\n zIndex: null,\n handlers: {}\n};\n\nfunction triggerHotkeys(e: KeyboardEvent) {\n if (state.zIndex === null) {\n return;\n }\n\n const keys = state.handlers[state.zIndex];\n for (const key in keys) {\n if (isHotkey(key, e)) {\n keys[key](e);\n break;\n }\n }\n}\n\nfunction registerZIndex({ zIndex, keys }: HookProps) {\n if (state.zIndex === null || state.zIndex < zIndex) {\n state.zIndex = zIndex;\n }\n\n if (!state.handlers[zIndex]) {\n state.handlers[zIndex] = {};\n }\n\n if (!keys || Object.keys(keys).length === 0) {\n return;\n }\n\n for (const key in keys) {\n if (key in state.handlers[zIndex]) {\n throw Error(`Shortcut \"${key}\" already registered on zIndex ${zIndex}.`);\n }\n state.handlers[zIndex][key] = keys[key];\n }\n\n if (!state.listenerAttached) {\n document.body.addEventListener(\"keydown\", triggerHotkeys);\n state.listenerAttached = true;\n }\n}\n\nfunction unregisterZIndex({ zIndex, keys }: HookProps) {\n if (state.handlers && state.handlers[zIndex]) {\n for (const key in keys) {\n delete state.handlers[zIndex][key];\n }\n\n if (Object.keys(state.handlers[zIndex]).length === 0) {\n delete state.handlers[zIndex];\n }\n }\n\n if (Object.keys(state.handlers).length > 0) {\n state.zIndex = Math.max(...Object.keys(state.handlers).map(Number));\n } else {\n state.zIndex = null;\n\n if (state.listenerAttached) {\n document.body.removeEventListener(\"keydown\", triggerHotkeys);\n state.listenerAttached = false;\n }\n }\n}\n\nexport function useHotkeys(props: HookProps) {\n const { disabled, zIndex, keys } = props;\n\n const prevPropsRef = useRef<HookProps | undefined>();\n const firstRenderRef = useRef(true);\n\n useEffect(\n function () {\n if (firstRenderRef.current || prevPropsRef.current?.disabled !== disabled) {\n firstRenderRef.current = false;\n if (disabled) {\n unregisterZIndex(props);\n } else {\n registerZIndex(props);\n }\n }\n\n if (!disabled && typeof keys === \"object\") {\n Object.assign(state.handlers[zIndex], keys);\n }\n prevPropsRef.current = { ...props };\n },\n [keys]\n );\n\n useEffect(function () {\n return function () {\n if (!disabled) {\n unregisterZIndex(
|
|
1
|
+
{"version":3,"file":"hooks/useHotkeys.js","sources":["../../src/hooks/useHotkeys.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport { isHotkey } from \"is-hotkey\";\n\ntype HookProps = {\n disabled?: boolean;\n zIndex: number;\n keys?: { [key: string]: (e: KeyboardEvent) => void };\n};\n\ntype State = {\n listenerAttached: boolean;\n zIndex: null | number;\n handlers: { [zIndex: number]: { [key: string]: (e: KeyboardEvent) => void } };\n};\n\nconst state: State = {\n listenerAttached: false,\n zIndex: null,\n handlers: {}\n};\n\nfunction triggerHotkeys(e: KeyboardEvent) {\n if (state.zIndex === null) {\n return;\n }\n\n const keys = state.handlers[state.zIndex];\n for (const key in keys) {\n if (isHotkey(key, e)) {\n keys[key](e);\n break;\n }\n }\n}\n\nfunction registerZIndex({ zIndex, keys }: HookProps) {\n if (state.zIndex === null || state.zIndex < zIndex) {\n state.zIndex = zIndex;\n }\n\n if (!state.handlers[zIndex]) {\n state.handlers[zIndex] = {};\n }\n\n if (!keys || Object.keys(keys).length === 0) {\n return;\n }\n\n for (const key in keys) {\n if (key in state.handlers[zIndex]) {\n throw Error(`Shortcut \"${key}\" already registered on zIndex ${zIndex}.`);\n }\n state.handlers[zIndex][key] = keys[key];\n }\n\n if (!state.listenerAttached) {\n document.body.addEventListener(\"keydown\", triggerHotkeys);\n state.listenerAttached = true;\n }\n}\n\nfunction unregisterZIndex({ zIndex, keys }: HookProps) {\n if (state.handlers && state.handlers[zIndex]) {\n for (const key in keys) {\n delete state.handlers[zIndex][key];\n }\n\n if (Object.keys(state.handlers[zIndex]).length === 0) {\n delete state.handlers[zIndex];\n }\n }\n\n if (Object.keys(state.handlers).length > 0) {\n state.zIndex = Math.max(...Object.keys(state.handlers).map(Number));\n } else {\n state.zIndex = null;\n\n if (state.listenerAttached) {\n document.body.removeEventListener(\"keydown\", triggerHotkeys);\n state.listenerAttached = false;\n }\n }\n}\n\nexport function useHotkeys(props: HookProps) {\n const { disabled, zIndex, keys } = props;\n\n const prevPropsRef = useRef<HookProps | undefined>();\n const firstRenderRef = useRef(true);\n\n // The unmount cleanup below runs once, so it must not close over the props of the first\n // render. Without this, a component unmounted while its shortcuts are registered (for\n // example, an open drawer navigating away) would leak them into the shared registry, and\n // the next component to register the same shortcut would throw.\n const propsRef = useRef(props);\n propsRef.current = props;\n\n useEffect(\n function () {\n if (firstRenderRef.current || prevPropsRef.current?.disabled !== disabled) {\n firstRenderRef.current = false;\n if (disabled) {\n unregisterZIndex(props);\n } else {\n registerZIndex(props);\n }\n }\n\n if (!disabled && typeof keys === \"object\") {\n Object.assign(state.handlers[zIndex], keys);\n }\n prevPropsRef.current = { ...props };\n },\n [keys]\n );\n\n useEffect(function () {\n return function () {\n if (!propsRef.current.disabled) {\n unregisterZIndex(propsRef.current);\n }\n };\n }, []);\n}\n"],"names":["state","triggerHotkeys","e","keys","key","isHotkey","registerZIndex","zIndex","Object","Error","document","unregisterZIndex","Math","Number","useHotkeys","props","disabled","prevPropsRef","useRef","firstRenderRef","propsRef","useEffect"],"mappings":";;AAeA,MAAMA,QAAe;IACjB,kBAAkB;IAClB,QAAQ;IACR,UAAU,CAAC;AACf;AAEA,SAASC,eAAeC,CAAgB;IACpC,IAAIF,AAAiB,SAAjBA,MAAM,MAAM,EACZ;IAGJ,MAAMG,OAAOH,MAAM,QAAQ,CAACA,MAAM,MAAM,CAAC;IACzC,IAAK,MAAMI,OAAOD,KACd,IAAIE,SAASD,KAAKF,IAAI;QAClBC,IAAI,CAACC,IAAI,CAACF;QACV;IACJ;AAER;AAEA,SAASI,eAAe,EAAEC,MAAM,EAAEJ,IAAI,EAAa;IAC/C,IAAIH,AAAiB,SAAjBA,MAAM,MAAM,IAAaA,MAAM,MAAM,GAAGO,QACxCP,MAAM,MAAM,GAAGO;IAGnB,IAAI,CAACP,MAAM,QAAQ,CAACO,OAAO,EACvBP,MAAM,QAAQ,CAACO,OAAO,GAAG,CAAC;IAG9B,IAAI,CAACJ,QAAQK,AAA6B,MAA7BA,OAAO,IAAI,CAACL,MAAM,MAAM,EACjC;IAGJ,IAAK,MAAMC,OAAOD,KAAM;QACpB,IAAIC,OAAOJ,MAAM,QAAQ,CAACO,OAAO,EAC7B,MAAME,MAAM,CAAC,UAAU,EAAEL,IAAI,+BAA+B,EAAEG,OAAO,CAAC,CAAC;QAE3EP,MAAM,QAAQ,CAACO,OAAO,CAACH,IAAI,GAAGD,IAAI,CAACC,IAAI;IAC3C;IAEA,IAAI,CAACJ,MAAM,gBAAgB,EAAE;QACzBU,SAAS,IAAI,CAAC,gBAAgB,CAAC,WAAWT;QAC1CD,MAAM,gBAAgB,GAAG;IAC7B;AACJ;AAEA,SAASW,iBAAiB,EAAEJ,MAAM,EAAEJ,IAAI,EAAa;IACjD,IAAIH,MAAM,QAAQ,IAAIA,MAAM,QAAQ,CAACO,OAAO,EAAE;QAC1C,IAAK,MAAMH,OAAOD,KACd,OAAOH,MAAM,QAAQ,CAACO,OAAO,CAACH,IAAI;QAGtC,IAAII,AAA+C,MAA/CA,OAAO,IAAI,CAACR,MAAM,QAAQ,CAACO,OAAO,EAAE,MAAM,EAC1C,OAAOP,MAAM,QAAQ,CAACO,OAAO;IAErC;IAEA,IAAIC,OAAO,IAAI,CAACR,MAAM,QAAQ,EAAE,MAAM,GAAG,GACrCA,MAAM,MAAM,GAAGY,KAAK,GAAG,IAAIJ,OAAO,IAAI,CAACR,MAAM,QAAQ,EAAE,GAAG,CAACa;SACxD;QACHb,MAAM,MAAM,GAAG;QAEf,IAAIA,MAAM,gBAAgB,EAAE;YACxBU,SAAS,IAAI,CAAC,mBAAmB,CAAC,WAAWT;YAC7CD,MAAM,gBAAgB,GAAG;QAC7B;IACJ;AACJ;AAEO,SAASc,WAAWC,KAAgB;IACvC,MAAM,EAAEC,QAAQ,EAAET,MAAM,EAAEJ,IAAI,EAAE,GAAGY;IAEnC,MAAME,eAAeC;IACrB,MAAMC,iBAAiBD,OAAO;IAM9B,MAAME,WAAWF,OAAOH;IACxBK,SAAS,OAAO,GAAGL;IAEnBM,UACI;QACI,IAAIF,eAAe,OAAO,IAAIF,aAAa,OAAO,EAAE,aAAaD,UAAU;YACvEG,eAAe,OAAO,GAAG;YACzB,IAAIH,UACAL,iBAAiBI;iBAEjBT,eAAeS;QAEvB;QAEA,IAAI,CAACC,YAAY,AAAgB,YAAhB,OAAOb,MACpBK,OAAO,MAAM,CAACR,MAAM,QAAQ,CAACO,OAAO,EAAEJ;QAE1Cc,aAAa,OAAO,GAAG;YAAE,GAAGF,KAAK;QAAC;IACtC,GACA;QAACZ;KAAK;IAGVkB,UAAU;QACN,OAAO;YACH,IAAI,CAACD,SAAS,OAAO,CAAC,QAAQ,EAC1BT,iBAAiBS,SAAS,OAAO;QAEzC;IACJ,GAAG,EAAE;AACT"}
|
package/index.d.ts
CHANGED
|
@@ -98,3 +98,4 @@ import "./base/Base/FieldRenderers/ObjectRenderer/ObjectAccordionMultipleRendere
|
|
|
98
98
|
import "./base/Base/FieldRenderers/ObjectRenderer/DynamicZoneRenderer.js";
|
|
99
99
|
import "./base/Base/FieldRenderers/ObjectRenderer/KeyValueTagsRenderer.js";
|
|
100
100
|
export type { FieldRenderers, FieldRendererComponent, LayoutRenderers, TabsNodeRendererProps } from "./features/formModel/FormView.js";
|
|
101
|
+
export { Debugger, DebuggerIndicator, DebuggerRoutes, debuggerStore } from "./features/debugger/index.js";
|
package/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "./base/Base/FieldRenderers/MultiSelectRenderer.js";
|
|
1
2
|
import "./base/Base/FieldRenderers/ObjectRenderer/KeyValueTagsRenderer.js";
|
|
2
3
|
import "./base/Base/FieldRenderers/ObjectRenderer/DynamicZoneRenderer.js";
|
|
3
4
|
import "./base/Base/FieldRenderers/ObjectRenderer/ObjectAccordionMultipleRenderer.js";
|
|
@@ -19,7 +20,6 @@ import "./base/Base/FieldRenderers/DateTimeRenderer.js";
|
|
|
19
20
|
import "./base/Base/FieldRenderers/CheckboxesRenderer.js";
|
|
20
21
|
import "./base/Base/FieldRenderers/RadioButtonsRenderer.js";
|
|
21
22
|
import "./base/Base/FieldRenderers/TagsRenderer.js";
|
|
22
|
-
import "./base/Base/FieldRenderers/TextareasRenderer.js";
|
|
23
23
|
import "./features/formModel/renderers.js";
|
|
24
24
|
import "./features/formModel/fieldTypes/TextFieldType.js";
|
|
25
25
|
import "./features/formModel/fieldTypes/NumberFieldType.js";
|
|
@@ -29,12 +29,12 @@ import "./features/formModel/fieldTypes/FileFieldType.js";
|
|
|
29
29
|
import "./features/formModel/fieldTypes/FileUrlFieldType.js";
|
|
30
30
|
import "./features/formModel/fieldTypes/ObjectFieldType.js";
|
|
31
31
|
import "./features/formModel/fieldTypes/LexicalFieldType.js";
|
|
32
|
+
import "./base/Base/FieldRenderers/TextareasRenderer.js";
|
|
32
33
|
import "./base/Base/FieldRenderers/TextInputsRenderer.js";
|
|
33
34
|
import "./base/Base/FieldRenderers/NumberInputsRenderer.js";
|
|
34
35
|
import "./base/Base/FieldRenderers/NumberInputRenderer.js";
|
|
35
36
|
import "./base/Base/FieldRenderers/SwitchRenderer.js";
|
|
36
37
|
import "./base/Base/FieldRenderers/TextareaRenderer.js";
|
|
37
|
-
import "./base/Base/FieldRenderers/MultiSelectRenderer.js";
|
|
38
38
|
import "./base/Base/FieldRenderers/InputRenderer.js";
|
|
39
39
|
import "./base/Base/FieldRenderers/SelectRenderer.js";
|
|
40
40
|
export * from "@webiny/app";
|
|
@@ -83,3 +83,4 @@ export { FormErrors } from "./features/formModel/FormErrors.js";
|
|
|
83
83
|
export { PresenterErrors } from "./features/formModel/PresenterErrors.js";
|
|
84
84
|
export { useFieldRenderers } from "./features/formModel/useFieldRenderers.js";
|
|
85
85
|
export { useLayoutRenderers } from "./features/formModel/useLayoutRenderers.js";
|
|
86
|
+
export { Debugger, DebuggerIndicator, DebuggerRoutes, debuggerStore } from "./features/debugger/index.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webiny/app-admin",
|
|
3
|
-
"version": "6.5.0-beta.
|
|
3
|
+
"version": "6.5.0-beta.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.js",
|
|
@@ -26,24 +26,24 @@
|
|
|
26
26
|
"@iconify/json": "2.2.503",
|
|
27
27
|
"@lexical/utils": "0.48.0",
|
|
28
28
|
"@types/react": "18.3.31",
|
|
29
|
-
"@webiny/admin-ui": "6.5.0-beta.
|
|
30
|
-
"@webiny/app": "6.5.0-beta.
|
|
29
|
+
"@webiny/admin-ui": "6.5.0-beta.2",
|
|
30
|
+
"@webiny/app": "6.5.0-beta.2",
|
|
31
31
|
"@webiny/di": "1.0.2",
|
|
32
|
-
"@webiny/feature": "6.5.0-beta.
|
|
33
|
-
"@webiny/form": "6.5.0-beta.
|
|
34
|
-
"@webiny/icons": "6.5.0-beta.
|
|
35
|
-
"@webiny/lexical-converter": "6.5.0-beta.
|
|
36
|
-
"@webiny/lexical-editor": "6.5.0-beta.
|
|
37
|
-
"@webiny/lexical-nodes": "6.5.0-beta.
|
|
38
|
-
"@webiny/lexical-theme": "6.5.0-beta.
|
|
39
|
-
"@webiny/plugins": "6.5.0-beta.
|
|
40
|
-
"@webiny/react-composition": "6.5.0-beta.
|
|
41
|
-
"@webiny/react-properties": "6.5.0-beta.
|
|
42
|
-
"@webiny/sdk": "6.5.0-beta.
|
|
43
|
-
"@webiny/telemetry": "6.5.0-beta.
|
|
44
|
-
"@webiny/utils": "6.5.0-beta.
|
|
45
|
-
"@webiny/validation": "6.5.0-beta.
|
|
46
|
-
"@webiny/wcp": "6.5.0-beta.
|
|
32
|
+
"@webiny/feature": "6.5.0-beta.2",
|
|
33
|
+
"@webiny/form": "6.5.0-beta.2",
|
|
34
|
+
"@webiny/icons": "6.5.0-beta.2",
|
|
35
|
+
"@webiny/lexical-converter": "6.5.0-beta.2",
|
|
36
|
+
"@webiny/lexical-editor": "6.5.0-beta.2",
|
|
37
|
+
"@webiny/lexical-nodes": "6.5.0-beta.2",
|
|
38
|
+
"@webiny/lexical-theme": "6.5.0-beta.2",
|
|
39
|
+
"@webiny/plugins": "6.5.0-beta.2",
|
|
40
|
+
"@webiny/react-composition": "6.5.0-beta.2",
|
|
41
|
+
"@webiny/react-properties": "6.5.0-beta.2",
|
|
42
|
+
"@webiny/sdk": "6.5.0-beta.2",
|
|
43
|
+
"@webiny/telemetry": "6.5.0-beta.2",
|
|
44
|
+
"@webiny/utils": "6.5.0-beta.2",
|
|
45
|
+
"@webiny/validation": "6.5.0-beta.2",
|
|
46
|
+
"@webiny/wcp": "6.5.0-beta.2",
|
|
47
47
|
"apollo-cache": "1.3.5",
|
|
48
48
|
"apollo-client": "2.6.10",
|
|
49
49
|
"apollo-link": "1.2.14",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"@types/react-transition-group": "4.4.12",
|
|
84
84
|
"@types/store": "2.0.5",
|
|
85
85
|
"@types/tinycolor2": "1.4.6",
|
|
86
|
-
"@webiny/build-tools": "6.5.0-beta.
|
|
86
|
+
"@webiny/build-tools": "6.5.0-beta.2",
|
|
87
87
|
"rimraf": "6.1.3",
|
|
88
88
|
"typescript": "7.0.2",
|
|
89
89
|
"vitest": "4.1.10"
|