@shopify/ui-extensions-server-kit 0.0.0-snapshot-20240814205424
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/CHANGELOG.md +127 -0
- package/README.md +74 -0
- package/dist/ExtensionServerClient/ExtensionServerClient.cjs.js +1 -0
- package/dist/ExtensionServerClient/ExtensionServerClient.d.ts +28 -0
- package/dist/ExtensionServerClient/ExtensionServerClient.es.js +133 -0
- package/dist/ExtensionServerClient/ExtensionServerClient.test.d.ts +1 -0
- package/dist/ExtensionServerClient/index.d.ts +2 -0
- package/dist/ExtensionServerClient/types.cjs.js +1 -0
- package/dist/ExtensionServerClient/types.d.ts +124 -0
- package/dist/ExtensionServerClient/types.es.js +4 -0
- package/dist/context/ExtensionServerProvider.cjs.js +1 -0
- package/dist/context/ExtensionServerProvider.d.ts +2 -0
- package/dist/context/ExtensionServerProvider.es.js +29 -0
- package/dist/context/ExtensionServerProvider.test.d.ts +1 -0
- package/dist/context/constants.cjs.js +1 -0
- package/dist/context/constants.d.ts +3 -0
- package/dist/context/constants.es.js +14 -0
- package/dist/context/index.d.ts +3 -0
- package/dist/context/types.d.ts +11 -0
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/useExtensionClient.cjs.js +1 -0
- package/dist/hooks/useExtensionClient.d.ts +1 -0
- package/dist/hooks/useExtensionClient.es.js +8 -0
- package/dist/hooks/useExtensionServerContext.cjs.js +1 -0
- package/dist/hooks/useExtensionServerContext.d.ts +1 -0
- package/dist/hooks/useExtensionServerContext.es.js +6 -0
- package/dist/hooks/useExtensionServerEvent.cjs.js +1 -0
- package/dist/hooks/useExtensionServerEvent.d.ts +1 -0
- package/dist/hooks/useExtensionServerEvent.es.js +9 -0
- package/dist/hooks/useExtensionServerState.cjs.js +1 -0
- package/dist/hooks/useExtensionServerState.d.ts +1 -0
- package/dist/hooks/useExtensionServerState.es.js +9 -0
- package/dist/hooks/useIsomorphicLayoutEffect.cjs.js +1 -0
- package/dist/hooks/useIsomorphicLayoutEffect.d.ts +2 -0
- package/dist/hooks/useIsomorphicLayoutEffect.es.js +5 -0
- package/dist/i18n.cjs.js +1 -0
- package/dist/i18n.d.ts +93 -0
- package/dist/i18n.es.js +61 -0
- package/dist/i18n.test.d.ts +1 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.cjs2.js +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.es.js +54 -0
- package/dist/index.es2.js +8 -0
- package/dist/state/actions/actions.cjs.js +1 -0
- package/dist/state/actions/actions.d.ts +6 -0
- package/dist/state/actions/actions.es.js +37 -0
- package/dist/state/actions/index.d.ts +2 -0
- package/dist/state/actions/types.d.ts +21 -0
- package/dist/state/index.d.ts +2 -0
- package/dist/state/reducers/constants.cjs.js +1 -0
- package/dist/state/reducers/constants.d.ts +2 -0
- package/dist/state/reducers/constants.es.js +7 -0
- package/dist/state/reducers/extensionServerReducer.cjs.js +1 -0
- package/dist/state/reducers/extensionServerReducer.d.ts +3 -0
- package/dist/state/reducers/extensionServerReducer.es.js +57 -0
- package/dist/state/reducers/extensionServerReducer.test.d.ts +1 -0
- package/dist/state/reducers/index.d.ts +3 -0
- package/dist/state/reducers/types.d.ts +6 -0
- package/dist/testing/MockExtensionServerProvider.cjs.js +1 -0
- package/dist/testing/MockExtensionServerProvider.d.ts +7 -0
- package/dist/testing/MockExtensionServerProvider.es.js +24 -0
- package/dist/testing/app.cjs.js +1 -0
- package/dist/testing/app.d.ts +2 -0
- package/dist/testing/app.es.js +16 -0
- package/dist/testing/extensions.cjs.js +1 -0
- package/dist/testing/extensions.d.ts +6 -0
- package/dist/testing/extensions.es.js +65 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/types.cjs.js +1 -0
- package/dist/types.d.ts +173 -0
- package/dist/types.es.js +4 -0
- package/dist/utilities/assetToString.cjs.js +1 -0
- package/dist/utilities/assetToString.d.ts +2 -0
- package/dist/utilities/assetToString.es.js +7 -0
- package/dist/utilities/assetToString.test.d.ts +1 -0
- package/dist/utilities/groupByKey.cjs.js +1 -0
- package/dist/utilities/groupByKey.d.ts +3 -0
- package/dist/utilities/groupByKey.es.js +6 -0
- package/dist/utilities/index.d.ts +7 -0
- package/dist/utilities/isUIExtension.cjs.js +1 -0
- package/dist/utilities/isUIExtension.d.ts +1 -0
- package/dist/utilities/isUIExtension.es.js +6 -0
- package/dist/utilities/isValidSurface.cjs.js +1 -0
- package/dist/utilities/isValidSurface.d.ts +2 -0
- package/dist/utilities/isValidSurface.es.js +7 -0
- package/dist/utilities/noop.cjs.js +1 -0
- package/dist/utilities/noop.d.ts +1 -0
- package/dist/utilities/noop.es.js +5 -0
- package/dist/utilities/replaceUpdated.cjs.js +1 -0
- package/dist/utilities/replaceUpdated.d.ts +1 -0
- package/dist/utilities/replaceUpdated.es.js +14 -0
- package/dist/utilities/replaceUpdated.test.d.ts +1 -0
- package/dist/utilities/set.cjs.js +1 -0
- package/dist/utilities/set.d.ts +4 -0
- package/dist/utilities/set.es.js +18 -0
- package/dist/utilities/set.test.d.ts +1 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/index.mjs +1 -0
- package/node_modules/@shopify/react-testing/LICENSE.md +21 -0
- package/node_modules/@shopify/react-testing/README.md +711 -0
- package/node_modules/@shopify/react-testing/build/cjs/TestWrapper.js +52 -0
- package/node_modules/@shopify/react-testing/build/cjs/_virtual/_rollupPluginBabelHelpers.js +47 -0
- package/node_modules/@shopify/react-testing/build/cjs/compat.js +14 -0
- package/node_modules/@shopify/react-testing/build/cjs/destroy.js +13 -0
- package/node_modules/@shopify/react-testing/build/cjs/element.js +225 -0
- package/node_modules/@shopify/react-testing/build/cjs/index.js +21 -0
- package/node_modules/@shopify/react-testing/build/cjs/matchers/components.js +46 -0
- package/node_modules/@shopify/react-testing/build/cjs/matchers/context.js +25 -0
- package/node_modules/@shopify/react-testing/build/cjs/matchers/index.js +16 -0
- package/node_modules/@shopify/react-testing/build/cjs/matchers/props.js +38 -0
- package/node_modules/@shopify/react-testing/build/cjs/matchers/strings.js +42 -0
- package/node_modules/@shopify/react-testing/build/cjs/matchers/utilities.js +110 -0
- package/node_modules/@shopify/react-testing/build/cjs/mount.js +76 -0
- package/node_modules/@shopify/react-testing/build/cjs/root.js +284 -0
- package/node_modules/@shopify/react-testing/build/cjs/toReactString.js +86 -0
- package/node_modules/@shopify/react-testing/build/cjs/types.js +28 -0
- package/node_modules/@shopify/react-testing/build/esm/TestWrapper.mjs +44 -0
- package/node_modules/@shopify/react-testing/build/esm/_virtual/_rollupPluginBabelHelpers.mjs +42 -0
- package/node_modules/@shopify/react-testing/build/esm/compat.mjs +10 -0
- package/node_modules/@shopify/react-testing/build/esm/destroy.mjs +9 -0
- package/node_modules/@shopify/react-testing/build/esm/element.mjs +221 -0
- package/node_modules/@shopify/react-testing/build/esm/index.mjs +5 -0
- package/node_modules/@shopify/react-testing/build/esm/matchers/components.mjs +41 -0
- package/node_modules/@shopify/react-testing/build/esm/matchers/context.mjs +21 -0
- package/node_modules/@shopify/react-testing/build/esm/matchers/index.mjs +14 -0
- package/node_modules/@shopify/react-testing/build/esm/matchers/props.mjs +33 -0
- package/node_modules/@shopify/react-testing/build/esm/matchers/strings.mjs +37 -0
- package/node_modules/@shopify/react-testing/build/esm/matchers/utilities.mjs +101 -0
- package/node_modules/@shopify/react-testing/build/esm/mount.mjs +70 -0
- package/node_modules/@shopify/react-testing/build/esm/root.mjs +275 -0
- package/node_modules/@shopify/react-testing/build/esm/toReactString.mjs +80 -0
- package/node_modules/@shopify/react-testing/build/esm/types.mjs +26 -0
- package/node_modules/@shopify/react-testing/build/esnext/TestWrapper.esnext +44 -0
- package/node_modules/@shopify/react-testing/build/esnext/compat.esnext +10 -0
- package/node_modules/@shopify/react-testing/build/esnext/destroy.esnext +9 -0
- package/node_modules/@shopify/react-testing/build/esnext/element.esnext +221 -0
- package/node_modules/@shopify/react-testing/build/esnext/index.esnext +5 -0
- package/node_modules/@shopify/react-testing/build/esnext/matchers/components.esnext +41 -0
- package/node_modules/@shopify/react-testing/build/esnext/matchers/context.esnext +21 -0
- package/node_modules/@shopify/react-testing/build/esnext/matchers/index.esnext +14 -0
- package/node_modules/@shopify/react-testing/build/esnext/matchers/props.esnext +33 -0
- package/node_modules/@shopify/react-testing/build/esnext/matchers/strings.esnext +37 -0
- package/node_modules/@shopify/react-testing/build/esnext/matchers/utilities.esnext +99 -0
- package/node_modules/@shopify/react-testing/build/esnext/mount.esnext +71 -0
- package/node_modules/@shopify/react-testing/build/esnext/root.esnext +275 -0
- package/node_modules/@shopify/react-testing/build/esnext/toReactString.esnext +80 -0
- package/node_modules/@shopify/react-testing/build/esnext/types.esnext +26 -0
- package/node_modules/@shopify/react-testing/build/ts/TestWrapper.d.ts +17 -0
- package/node_modules/@shopify/react-testing/build/ts/TestWrapper.d.ts.map +1 -0
- package/node_modules/@shopify/react-testing/build/ts/compat.d.ts +3 -0
- package/node_modules/@shopify/react-testing/build/ts/compat.d.ts.map +1 -0
- package/node_modules/@shopify/react-testing/build/ts/destroy.d.ts +2 -0
- package/node_modules/@shopify/react-testing/build/ts/destroy.d.ts.map +1 -0
- package/node_modules/@shopify/react-testing/build/ts/element.d.ts +42 -0
- package/node_modules/@shopify/react-testing/build/ts/element.d.ts.map +1 -0
- package/node_modules/@shopify/react-testing/build/ts/index.d.ts +7 -0
- package/node_modules/@shopify/react-testing/build/ts/index.d.ts.map +1 -0
- package/node_modules/@shopify/react-testing/build/ts/matchers/components.d.ts +12 -0
- package/node_modules/@shopify/react-testing/build/ts/matchers/components.d.ts.map +1 -0
- package/node_modules/@shopify/react-testing/build/ts/matchers/context.d.ts +8 -0
- package/node_modules/@shopify/react-testing/build/ts/matchers/context.d.ts.map +1 -0
- package/node_modules/@shopify/react-testing/build/ts/matchers/index.d.ts +20 -0
- package/node_modules/@shopify/react-testing/build/ts/matchers/index.d.ts.map +1 -0
- package/node_modules/@shopify/react-testing/build/ts/matchers/props.d.ts +10 -0
- package/node_modules/@shopify/react-testing/build/ts/matchers/props.d.ts.map +1 -0
- package/node_modules/@shopify/react-testing/build/ts/matchers/strings.d.ts +11 -0
- package/node_modules/@shopify/react-testing/build/ts/matchers/strings.d.ts.map +1 -0
- package/node_modules/@shopify/react-testing/build/ts/matchers/utilities.d.ts +17 -0
- package/node_modules/@shopify/react-testing/build/ts/matchers/utilities.d.ts.map +1 -0
- package/node_modules/@shopify/react-testing/build/ts/mount.d.ts +39 -0
- package/node_modules/@shopify/react-testing/build/ts/mount.d.ts.map +1 -0
- package/node_modules/@shopify/react-testing/build/ts/root.d.ts +55 -0
- package/node_modules/@shopify/react-testing/build/ts/root.d.ts.map +1 -0
- package/node_modules/@shopify/react-testing/build/ts/toReactString.d.ts +5 -0
- package/node_modules/@shopify/react-testing/build/ts/toReactString.d.ts.map +1 -0
- package/node_modules/@shopify/react-testing/build/ts/types.d.ts +89 -0
- package/node_modules/@shopify/react-testing/build/ts/types.d.ts.map +1 -0
- package/node_modules/@shopify/react-testing/index.esnext +1 -0
- package/node_modules/@shopify/react-testing/index.js +1 -0
- package/node_modules/@shopify/react-testing/index.mjs +1 -0
- package/node_modules/@shopify/react-testing/matchers.esnext +1 -0
- package/node_modules/@shopify/react-testing/matchers.js +1 -0
- package/node_modules/@shopify/react-testing/matchers.mjs +1 -0
- package/node_modules/@shopify/react-testing/package.json +69 -0
- package/node_modules/@shopify/ui-extensions-test-utils/CHANGELOG.md +66 -0
- package/node_modules/@shopify/ui-extensions-test-utils/dist/index.js +3 -0
- package/node_modules/@shopify/ui-extensions-test-utils/dist/render.js +5 -0
- package/node_modules/@shopify/ui-extensions-test-utils/dist/renderHook.js +20 -0
- package/node_modules/@shopify/ui-extensions-test-utils/dist/withProviders.js +6 -0
- package/node_modules/@shopify/ui-extensions-test-utils/package.json +41 -0
- package/node_modules/@shopify/ui-extensions-test-utils/project.json +39 -0
- package/node_modules/@types/node/LICENSE +21 -0
- package/node_modules/@types/node/README.md +15 -0
- package/node_modules/@types/node/assert/strict.d.ts +8 -0
- package/node_modules/@types/node/assert.d.ts +985 -0
- package/node_modules/@types/node/async_hooks.d.ts +522 -0
- package/node_modules/@types/node/buffer.d.ts +2321 -0
- package/node_modules/@types/node/child_process.d.ts +1544 -0
- package/node_modules/@types/node/cluster.d.ts +432 -0
- package/node_modules/@types/node/console.d.ts +412 -0
- package/node_modules/@types/node/constants.d.ts +19 -0
- package/node_modules/@types/node/crypto.d.ts +4451 -0
- package/node_modules/@types/node/dgram.d.ts +586 -0
- package/node_modules/@types/node/diagnostics_channel.d.ts +192 -0
- package/node_modules/@types/node/dns/promises.d.ts +381 -0
- package/node_modules/@types/node/dns.d.ts +809 -0
- package/node_modules/@types/node/dom-events.d.ts +122 -0
- package/node_modules/@types/node/domain.d.ts +170 -0
- package/node_modules/@types/node/events.d.ts +803 -0
- package/node_modules/@types/node/fs/promises.d.ts +1205 -0
- package/node_modules/@types/node/fs.d.ts +4211 -0
- package/node_modules/@types/node/globals.d.ts +377 -0
- package/node_modules/@types/node/globals.global.d.ts +1 -0
- package/node_modules/@types/node/http.d.ts +1801 -0
- package/node_modules/@types/node/http2.d.ts +2386 -0
- package/node_modules/@types/node/https.d.ts +544 -0
- package/node_modules/@types/node/index.d.ts +88 -0
- package/node_modules/@types/node/inspector.d.ts +2739 -0
- package/node_modules/@types/node/module.d.ts +298 -0
- package/node_modules/@types/node/net.d.ts +913 -0
- package/node_modules/@types/node/os.d.ts +473 -0
- package/node_modules/@types/node/package.json +235 -0
- package/node_modules/@types/node/path.d.ts +191 -0
- package/node_modules/@types/node/perf_hooks.d.ts +626 -0
- package/node_modules/@types/node/process.d.ts +1531 -0
- package/node_modules/@types/node/punycode.d.ts +117 -0
- package/node_modules/@types/node/querystring.d.ts +141 -0
- package/node_modules/@types/node/readline/promises.d.ts +143 -0
- package/node_modules/@types/node/readline.d.ts +666 -0
- package/node_modules/@types/node/repl.d.ts +430 -0
- package/node_modules/@types/node/stream/consumers.d.ts +12 -0
- package/node_modules/@types/node/stream/promises.d.ts +83 -0
- package/node_modules/@types/node/stream/web.d.ts +336 -0
- package/node_modules/@types/node/stream.d.ts +1731 -0
- package/node_modules/@types/node/string_decoder.d.ts +67 -0
- package/node_modules/@types/node/test.d.ts +1113 -0
- package/node_modules/@types/node/timers/promises.d.ts +93 -0
- package/node_modules/@types/node/timers.d.ts +126 -0
- package/node_modules/@types/node/tls.d.ts +1203 -0
- package/node_modules/@types/node/trace_events.d.ts +171 -0
- package/node_modules/@types/node/ts4.8/assert/strict.d.ts +8 -0
- package/node_modules/@types/node/ts4.8/assert.d.ts +985 -0
- package/node_modules/@types/node/ts4.8/async_hooks.d.ts +522 -0
- package/node_modules/@types/node/ts4.8/buffer.d.ts +2321 -0
- package/node_modules/@types/node/ts4.8/child_process.d.ts +1544 -0
- package/node_modules/@types/node/ts4.8/cluster.d.ts +432 -0
- package/node_modules/@types/node/ts4.8/console.d.ts +412 -0
- package/node_modules/@types/node/ts4.8/constants.d.ts +19 -0
- package/node_modules/@types/node/ts4.8/crypto.d.ts +4450 -0
- package/node_modules/@types/node/ts4.8/dgram.d.ts +586 -0
- package/node_modules/@types/node/ts4.8/diagnostics_channel.d.ts +192 -0
- package/node_modules/@types/node/ts4.8/dns/promises.d.ts +381 -0
- package/node_modules/@types/node/ts4.8/dns.d.ts +809 -0
- package/node_modules/@types/node/ts4.8/dom-events.d.ts +122 -0
- package/node_modules/@types/node/ts4.8/domain.d.ts +170 -0
- package/node_modules/@types/node/ts4.8/events.d.ts +754 -0
- package/node_modules/@types/node/ts4.8/fs/promises.d.ts +1205 -0
- package/node_modules/@types/node/ts4.8/fs.d.ts +4211 -0
- package/node_modules/@types/node/ts4.8/globals.d.ts +377 -0
- package/node_modules/@types/node/ts4.8/globals.global.d.ts +1 -0
- package/node_modules/@types/node/ts4.8/http.d.ts +1801 -0
- package/node_modules/@types/node/ts4.8/http2.d.ts +2386 -0
- package/node_modules/@types/node/ts4.8/https.d.ts +544 -0
- package/node_modules/@types/node/ts4.8/index.d.ts +88 -0
- package/node_modules/@types/node/ts4.8/inspector.d.ts +2739 -0
- package/node_modules/@types/node/ts4.8/module.d.ts +298 -0
- package/node_modules/@types/node/ts4.8/net.d.ts +913 -0
- package/node_modules/@types/node/ts4.8/os.d.ts +473 -0
- package/node_modules/@types/node/ts4.8/path.d.ts +191 -0
- package/node_modules/@types/node/ts4.8/perf_hooks.d.ts +626 -0
- package/node_modules/@types/node/ts4.8/process.d.ts +1531 -0
- package/node_modules/@types/node/ts4.8/punycode.d.ts +117 -0
- package/node_modules/@types/node/ts4.8/querystring.d.ts +141 -0
- package/node_modules/@types/node/ts4.8/readline/promises.d.ts +143 -0
- package/node_modules/@types/node/ts4.8/readline.d.ts +666 -0
- package/node_modules/@types/node/ts4.8/repl.d.ts +430 -0
- package/node_modules/@types/node/ts4.8/stream/consumers.d.ts +12 -0
- package/node_modules/@types/node/ts4.8/stream/promises.d.ts +83 -0
- package/node_modules/@types/node/ts4.8/stream/web.d.ts +336 -0
- package/node_modules/@types/node/ts4.8/stream.d.ts +1731 -0
- package/node_modules/@types/node/ts4.8/string_decoder.d.ts +67 -0
- package/node_modules/@types/node/ts4.8/test.d.ts +1113 -0
- package/node_modules/@types/node/ts4.8/timers/promises.d.ts +93 -0
- package/node_modules/@types/node/ts4.8/timers.d.ts +126 -0
- package/node_modules/@types/node/ts4.8/tls.d.ts +1203 -0
- package/node_modules/@types/node/ts4.8/trace_events.d.ts +171 -0
- package/node_modules/@types/node/ts4.8/tty.d.ts +206 -0
- package/node_modules/@types/node/ts4.8/url.d.ts +937 -0
- package/node_modules/@types/node/ts4.8/util.d.ts +2075 -0
- package/node_modules/@types/node/ts4.8/v8.d.ts +541 -0
- package/node_modules/@types/node/ts4.8/vm.d.ts +667 -0
- package/node_modules/@types/node/ts4.8/wasi.d.ts +158 -0
- package/node_modules/@types/node/ts4.8/worker_threads.d.ts +692 -0
- package/node_modules/@types/node/ts4.8/zlib.d.ts +517 -0
- package/node_modules/@types/node/tty.d.ts +206 -0
- package/node_modules/@types/node/url.d.ts +937 -0
- package/node_modules/@types/node/util.d.ts +2075 -0
- package/node_modules/@types/node/v8.d.ts +541 -0
- package/node_modules/@types/node/vm.d.ts +667 -0
- package/node_modules/@types/node/wasi.d.ts +158 -0
- package/node_modules/@types/node/worker_threads.d.ts +692 -0
- package/node_modules/@types/node/zlib.d.ts +517 -0
- package/node_modules/@types/react/LICENSE +21 -0
- package/node_modules/@types/react/README.md +16 -0
- package/node_modules/@types/react/experimental.d.ts +192 -0
- package/node_modules/@types/react/global.d.ts +151 -0
- package/node_modules/@types/react/index.d.ts +3175 -0
- package/node_modules/@types/react/jsx-dev-runtime.d.ts +2 -0
- package/node_modules/@types/react/jsx-runtime.d.ts +2 -0
- package/node_modules/@types/react/package.json +149 -0
- package/node_modules/@vitejs/plugin-react-refresh/LICENSE +21 -0
- package/node_modules/@vitejs/plugin-react-refresh/README.md +73 -0
- package/node_modules/@vitejs/plugin-react-refresh/index.d.ts +14 -0
- package/node_modules/@vitejs/plugin-react-refresh/index.js +239 -0
- package/node_modules/@vitejs/plugin-react-refresh/package.json +35 -0
- package/package.json +64 -0
- package/project.json +74 -0
- package/scripts/create-entry-files.ts +44 -0
- package/src/ExtensionServerClient/ExtensionServerClient.test.ts +730 -0
- package/src/ExtensionServerClient/ExtensionServerClient.ts +310 -0
- package/src/ExtensionServerClient/index.ts +2 -0
- package/src/ExtensionServerClient/types.ts +159 -0
- package/src/context/ExtensionServerProvider.test.tsx +173 -0
- package/src/context/ExtensionServerProvider.tsx +46 -0
- package/src/context/constants.ts +15 -0
- package/src/context/index.ts +3 -0
- package/src/context/types.ts +13 -0
- package/src/hooks/index.ts +5 -0
- package/src/hooks/useExtensionClient.ts +6 -0
- package/src/hooks/useExtensionServerContext.ts +4 -0
- package/src/hooks/useExtensionServerEvent.ts +11 -0
- package/src/hooks/useExtensionServerState.ts +6 -0
- package/src/hooks/useIsomorphicLayoutEffect.ts +6 -0
- package/src/i18n.test.ts +417 -0
- package/src/i18n.ts +208 -0
- package/src/index.ts +7 -0
- package/src/state/actions/actions.ts +36 -0
- package/src/state/actions/index.ts +2 -0
- package/src/state/actions/types.ts +26 -0
- package/src/state/index.ts +2 -0
- package/src/state/reducers/constants.ts +6 -0
- package/src/state/reducers/extensionServerReducer.test.ts +160 -0
- package/src/state/reducers/extensionServerReducer.ts +87 -0
- package/src/state/reducers/index.ts +3 -0
- package/src/state/reducers/types.ts +7 -0
- package/src/testing/MockExtensionServerProvider.tsx +36 -0
- package/src/testing/app.ts +15 -0
- package/src/testing/extensions.ts +70 -0
- package/src/testing/index.ts +3 -0
- package/src/types.ts +177 -0
- package/src/utilities/assetToString.test.ts +16 -0
- package/src/utilities/assetToString.ts +8 -0
- package/src/utilities/groupByKey.ts +3 -0
- package/src/utilities/index.ts +7 -0
- package/src/utilities/isUIExtension.ts +7 -0
- package/src/utilities/isValidSurface.ts +7 -0
- package/src/utilities/noop.ts +1 -0
- package/src/utilities/replaceUpdated.test.ts +26 -0
- package/src/utilities/replaceUpdated.ts +16 -0
- package/src/utilities/set.test.ts +19 -0
- package/src/utilities/set.ts +29 -0
- package/testing.d.ts +1 -0
- package/testing.js +1 -0
- package/testing.mjs +1 -0
- package/tests/setup.ts +6 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import {Surface} from './types.js'
|
|
3
|
+
import {
|
|
4
|
+
FlattenedLocalization,
|
|
5
|
+
Localization,
|
|
6
|
+
TRANSLATED_KEYS,
|
|
7
|
+
getFlattenedLocalization,
|
|
8
|
+
isFlattenedTranslations,
|
|
9
|
+
} from '../i18n'
|
|
10
|
+
import {isUIExtension, isValidSurface} from '../utilities'
|
|
11
|
+
import {DeepPartial, ExtensionPayload, ExtensionPoint} from '../types'
|
|
12
|
+
|
|
13
|
+
export class ExtensionServerClient implements ExtensionServer.Client {
|
|
14
|
+
public id: string
|
|
15
|
+
|
|
16
|
+
public connection!: WebSocket
|
|
17
|
+
|
|
18
|
+
public options: ExtensionServer.Options
|
|
19
|
+
|
|
20
|
+
protected EVENT_THAT_WILL_MUTATE_THE_SERVER = ['update']
|
|
21
|
+
|
|
22
|
+
protected listeners: {[key: string]: Set<any>} = {}
|
|
23
|
+
protected connectionListeners: {close: Set<any>; open: Set<any>} = {close: new Set(), open: new Set()}
|
|
24
|
+
|
|
25
|
+
protected connected = false
|
|
26
|
+
|
|
27
|
+
private uiExtensionsByUuid: {[key: string]: ExtensionServer.UIExtension} = {}
|
|
28
|
+
|
|
29
|
+
constructor(options: DeepPartial<ExtensionServer.Options> = {}) {
|
|
30
|
+
this.id = (Math.random() + 1).toString(36).substring(7)
|
|
31
|
+
this.options = getValidatedOptions({
|
|
32
|
+
...options,
|
|
33
|
+
connection: {
|
|
34
|
+
automaticConnect: true,
|
|
35
|
+
protocols: [],
|
|
36
|
+
...(options.connection ?? {}),
|
|
37
|
+
},
|
|
38
|
+
}) as ExtensionServer.Options
|
|
39
|
+
|
|
40
|
+
this.setupConnection(this.options.connection.automaticConnect)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public connect(options: ExtensionServer.Options = {connection: {}}) {
|
|
44
|
+
const newOptions = mergeOptions(this.options, options)
|
|
45
|
+
const optionsChanged = JSON.stringify(newOptions) !== JSON.stringify(this.options)
|
|
46
|
+
|
|
47
|
+
if (optionsChanged) {
|
|
48
|
+
this.options = newOptions
|
|
49
|
+
this.setupConnection(true)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return () => {
|
|
53
|
+
this.closeConnection()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public on<TEvent extends keyof ExtensionServer.InboundEvents>(
|
|
58
|
+
event: TEvent,
|
|
59
|
+
listener: (payload: ExtensionServer.InboundEvents[TEvent]) => void,
|
|
60
|
+
): () => void {
|
|
61
|
+
if (!this.listeners[event]) {
|
|
62
|
+
this.listeners[event] = new Set()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.listeners[event].add(listener)
|
|
66
|
+
return () => this.listeners[event].delete(listener)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public persist<TEvent extends keyof ExtensionServer.OutboundPersistEvents>(
|
|
70
|
+
event: TEvent,
|
|
71
|
+
data: ExtensionServer.OutboundPersistEvents[TEvent],
|
|
72
|
+
): void {
|
|
73
|
+
if (this.EVENT_THAT_WILL_MUTATE_THE_SERVER.includes(event)) {
|
|
74
|
+
if (!this.options.locales) {
|
|
75
|
+
return this.connection?.send(JSON.stringify({event, data}))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Since each websocket connection will have its own translated values
|
|
80
|
+
* we need to strip out all translated properties to prevent
|
|
81
|
+
* mutating the Dev Server's data
|
|
82
|
+
* Before:
|
|
83
|
+
* ```
|
|
84
|
+
* {
|
|
85
|
+
* name: 'en name'
|
|
86
|
+
* localization: {...},
|
|
87
|
+
* extensionPoints: [{
|
|
88
|
+
* target: 'admin.product.item.action'
|
|
89
|
+
* name: 'en name'
|
|
90
|
+
* description: 'en description'
|
|
91
|
+
* localization: {...}
|
|
92
|
+
* }],
|
|
93
|
+
* }
|
|
94
|
+
* ```
|
|
95
|
+
* After:
|
|
96
|
+
* ```
|
|
97
|
+
* extensionPoints: [{
|
|
98
|
+
* target: 'admin.product.item.action'
|
|
99
|
+
* }],
|
|
100
|
+
* }
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
data.extensions?.forEach((extension) => {
|
|
104
|
+
TRANSLATED_KEYS.forEach((key) => {
|
|
105
|
+
if (isUIExtension(extension)) {
|
|
106
|
+
extension.extensionPoints?.forEach((extensionPoint) => {
|
|
107
|
+
delete extensionPoint[key as keyof ExtensionPoint]
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
delete extension[key as keyof ExtensionPayload]
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
return this.connection?.send(JSON.stringify({event, data}))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
console.warn(`You tried to use "persist" with a dispatch event. Please use the "emit" method instead.`)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public emit<TEvent extends keyof ExtensionServer.DispatchEvents>(...args: ExtensionServer.EmitArgs<TEvent>): void {
|
|
120
|
+
const [event, data] = args
|
|
121
|
+
|
|
122
|
+
if (this.EVENT_THAT_WILL_MUTATE_THE_SERVER.includes(event)) {
|
|
123
|
+
return console.warn(
|
|
124
|
+
`You tried to use "emit" with a the "${event}" event. Please use the "persist" method instead to persist changes to the server.`,
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this.connection?.send(JSON.stringify({event: 'dispatch', data: {type: event, payload: data}}))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public onConnection<TEvent extends keyof typeof this.connectionListeners>(
|
|
132
|
+
event: TEvent,
|
|
133
|
+
listener: (event: Event) => void,
|
|
134
|
+
): () => void {
|
|
135
|
+
this.connectionListeners[event].add(listener)
|
|
136
|
+
return () => this.connectionListeners[event].delete(listener)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
protected initializeConnection() {
|
|
140
|
+
if (!this.connection) {
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
this.connection.addEventListener('open', (event) => {
|
|
145
|
+
this.connected = true
|
|
146
|
+
this.connectionListeners.open.forEach((listener) => listener(event))
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
this.connection.addEventListener('close', (event) => {
|
|
150
|
+
this.connected = false
|
|
151
|
+
this.connectionListeners.close.forEach((listener) => listener(event))
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
this.connection?.addEventListener('message', (message) => {
|
|
155
|
+
try {
|
|
156
|
+
const {event, data} = JSON.parse(message.data) as ExtensionServer.ServerEvents
|
|
157
|
+
|
|
158
|
+
if (event === 'dispatch') {
|
|
159
|
+
const {type, payload} = data
|
|
160
|
+
this.listeners[type]?.forEach((listener) => listener(payload))
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const filteredExtensions = data.extensions
|
|
165
|
+
? filterExtensionsBySurface(data.extensions, this.options.surface)
|
|
166
|
+
: data.extensions
|
|
167
|
+
|
|
168
|
+
this.listeners[event]?.forEach((listener) => {
|
|
169
|
+
listener({...data, extensions: this._getLocalizedExtensions(filteredExtensions)})
|
|
170
|
+
})
|
|
171
|
+
// eslint-disable-next-line no-catch-all/no-catch-all
|
|
172
|
+
} catch (err) {
|
|
173
|
+
console.error(
|
|
174
|
+
`[ExtensionServer] Something went wrong while parsing a server message:`,
|
|
175
|
+
err instanceof Error ? err.message : err,
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
protected setupConnection(connectWebsocket = true) {
|
|
182
|
+
if (!this.options.connection.url) {
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (!connectWebsocket) {
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
this.closeConnection()
|
|
191
|
+
|
|
192
|
+
this.connection = new WebSocket(this.options.connection.url, this.options.connection.protocols)
|
|
193
|
+
|
|
194
|
+
this.initializeConnection()
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
protected closeConnection() {
|
|
198
|
+
if (this.connected) {
|
|
199
|
+
this.connection?.close()
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private _getLocalizedExtensions(extensions?: ExtensionPayload[]) {
|
|
204
|
+
return extensions?.map((extension) => {
|
|
205
|
+
if (!this.options.locales || !isUIExtension(extension)) {
|
|
206
|
+
return extension
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const shouldUpdateTranslations =
|
|
210
|
+
this.uiExtensionsByUuid[extension.uuid]?.localization?.lastUpdated !== extension.localization?.lastUpdated
|
|
211
|
+
|
|
212
|
+
const localization = shouldUpdateTranslations
|
|
213
|
+
? getFlattenedLocalization(extension.localization, this.options.locales)
|
|
214
|
+
: this.uiExtensionsByUuid[extension.uuid]?.localization || extension.localization
|
|
215
|
+
|
|
216
|
+
const parsedTranslation: {[key: string]: string} =
|
|
217
|
+
localization && isFlattenedTranslations(localization) ? JSON.parse(localization.translations) : localization
|
|
218
|
+
|
|
219
|
+
const localizedExtension = {
|
|
220
|
+
...extension,
|
|
221
|
+
localization,
|
|
222
|
+
name:
|
|
223
|
+
parsedTranslation && extension.name.startsWith('t:')
|
|
224
|
+
? this._getLocalizedValue(parsedTranslation, extension.name)
|
|
225
|
+
: extension.name,
|
|
226
|
+
...(extension.description && {
|
|
227
|
+
description:
|
|
228
|
+
parsedTranslation && extension.description?.startsWith('t:')
|
|
229
|
+
? this._getLocalizedValue(parsedTranslation, extension.description)
|
|
230
|
+
: extension.description,
|
|
231
|
+
}),
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
this.uiExtensionsByUuid[extension.uuid] = {
|
|
235
|
+
...localizedExtension,
|
|
236
|
+
extensionPoints: this._getLocalizedExtensionPoints(localization, localizedExtension),
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return this.uiExtensionsByUuid[extension.uuid]
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private _getLocalizedExtensionPoints(
|
|
244
|
+
localization: FlattenedLocalization | Localization | null | undefined,
|
|
245
|
+
{extensionPoints, name, description}: ExtensionServer.UIExtension,
|
|
246
|
+
): ExtensionPoint[] {
|
|
247
|
+
if (!localization || !isFlattenedTranslations(localization)) {
|
|
248
|
+
return extensionPoints
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return extensionPoints?.map((extensionPoint) => {
|
|
252
|
+
return {
|
|
253
|
+
...extensionPoint,
|
|
254
|
+
localization,
|
|
255
|
+
name,
|
|
256
|
+
...(description && {description}),
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private _getLocalizedValue(translations: {[x: string]: string}, value: string): string {
|
|
262
|
+
const translationKey = value.replace('t:', '')
|
|
263
|
+
return translations[translationKey] || value
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function mergeOptions(options: ExtensionServer.Options, newOptions: ExtensionServer.Options) {
|
|
268
|
+
return getValidatedOptions({
|
|
269
|
+
...options,
|
|
270
|
+
...newOptions,
|
|
271
|
+
connection: {
|
|
272
|
+
...options.connection,
|
|
273
|
+
...newOptions.connection,
|
|
274
|
+
},
|
|
275
|
+
})
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function getValidatedOptions<TOptions extends DeepPartial<ExtensionServer.Options>>(options: TOptions): TOptions {
|
|
279
|
+
if (!isValidSurface(options.surface)) {
|
|
280
|
+
delete options.surface
|
|
281
|
+
}
|
|
282
|
+
return options
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function filterExtensionsBySurface(extensions: ExtensionPayload[], surface: Surface | undefined): ExtensionPayload[] {
|
|
286
|
+
if (!surface) {
|
|
287
|
+
return extensions
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return extensions.filter((extension) => {
|
|
291
|
+
if (extension.surface === surface) {
|
|
292
|
+
return true
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (Array.isArray(extension.extensionPoints)) {
|
|
296
|
+
const extensionPoints: (string | {surface: Surface; [key: string]: any})[] = extension.extensionPoints
|
|
297
|
+
const extensionPointMatchingSurface = extensionPoints.filter((extensionPoint) => {
|
|
298
|
+
if (typeof extensionPoint === 'string') {
|
|
299
|
+
return false
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return extensionPoint.surface === surface
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
return extensionPointMatchingSurface.length > 0
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return false
|
|
309
|
+
})
|
|
310
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type {LocalesOptions} from '../i18n'
|
|
2
|
+
|
|
3
|
+
declare global {
|
|
4
|
+
namespace ExtensionServer {
|
|
5
|
+
/**
|
|
6
|
+
* Events being received by the extension server where the keys are the event names
|
|
7
|
+
* and the values are the payload of the given action. In case no payload is
|
|
8
|
+
* required, a value of void should be used.
|
|
9
|
+
*/
|
|
10
|
+
interface InboundEvents {
|
|
11
|
+
//
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Events being sent to the extension server where the keys are the event names
|
|
16
|
+
* and the values are the payload of the given action. In case no payload is
|
|
17
|
+
* required, a value of void should be used.
|
|
18
|
+
*
|
|
19
|
+
* Persist events are those that will generate changes on the server, like
|
|
20
|
+
* update and connected events. Dispatch events are those that will
|
|
21
|
+
* simply be proxied to the clients connected to the server.
|
|
22
|
+
*/
|
|
23
|
+
interface OutboundPersistEvents {
|
|
24
|
+
//
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface DispatchEvents {
|
|
28
|
+
//
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Extension server client class options. These are used to configure
|
|
33
|
+
* the client class.
|
|
34
|
+
*/
|
|
35
|
+
interface Options {
|
|
36
|
+
connection: {
|
|
37
|
+
/**
|
|
38
|
+
* The absolute URL of the WebSocket.
|
|
39
|
+
*/
|
|
40
|
+
url?: string
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* This defines if we should automatically attempt to connect when the
|
|
44
|
+
* class is instantiated.
|
|
45
|
+
*
|
|
46
|
+
* @defaultValue true
|
|
47
|
+
*/
|
|
48
|
+
automaticConnect?: boolean
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The sub-protocol selected by the server.
|
|
52
|
+
*
|
|
53
|
+
* @defaultValue []
|
|
54
|
+
*/
|
|
55
|
+
protocols?: string | string[]
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* If provided the extension server will only return extensions that matches the specified surface
|
|
59
|
+
*/
|
|
60
|
+
surface?: Surface
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* If provided the extension server will return a requested translations object with flattened
|
|
64
|
+
* translations for each extension matching the requested locales
|
|
65
|
+
*/
|
|
66
|
+
locales?: LocalesOptions
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Extension server client class. This class will be used to connect and
|
|
71
|
+
* communicate with the extension server.
|
|
72
|
+
*/
|
|
73
|
+
interface Client {
|
|
74
|
+
/**
|
|
75
|
+
* Connection options
|
|
76
|
+
*/
|
|
77
|
+
options: Options
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Reconnecting WebSocket Client
|
|
81
|
+
*/
|
|
82
|
+
connection: WebSocket
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Function to add an event listener to messages coming from
|
|
86
|
+
* the extension server connection.
|
|
87
|
+
*/
|
|
88
|
+
on<TEvent extends keyof ExtensionServer.InboundEvents>(
|
|
89
|
+
event: TEvent,
|
|
90
|
+
cb: EventListener<TEvent>,
|
|
91
|
+
): EventUnsubscriber
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Function to emit an event that will persist changes to the extension server.
|
|
95
|
+
*/
|
|
96
|
+
persist<TEvent extends keyof OutboundPersistEvents>(event: TEvent, payload: OutboundPersistEvents[TEvent]): void
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Function to emit an event to the extension server.
|
|
100
|
+
*/
|
|
101
|
+
emit<TEvent extends keyof DispatchEvents>(...args: EmitArgs<TEvent>): void
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Function that opens a connection with the extensions server.
|
|
105
|
+
*/
|
|
106
|
+
connect(options?: Options): () => void
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* This defines how the ExtensionServer client's static class is defined and the constructor
|
|
111
|
+
* arguments it requires.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```
|
|
115
|
+
* const client = new ExtensionServer({ url: 'wss://localhost:1234' });
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
type StaticClient = Static<ExtensionServer.Client, [option?: ExtensionServer.Options]>
|
|
119
|
+
|
|
120
|
+
// Utilities
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* This helper type allows us to account for nullish payloads on the emit function.
|
|
124
|
+
* In practice, this will allow TypeScript to type-check the event being emitted
|
|
125
|
+
* and, if the payload isn't required, the second argument won't be necessary.
|
|
126
|
+
*/
|
|
127
|
+
type EmitArgs<TEvent extends keyof ExtensionServer.DispatchEvents> =
|
|
128
|
+
ExtensionServer.DispatchEvents[TEvent] extends void
|
|
129
|
+
? [event: TEvent]
|
|
130
|
+
: [event: TEvent, payload: ExtensionServer.DispatchEvents[TEvent]]
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* This is a helper interface that allows us to define the static methods of a given
|
|
134
|
+
* class. This is useful to define static methods, static properties
|
|
135
|
+
* and constructor variables.
|
|
136
|
+
*/
|
|
137
|
+
interface Static<T = unknown, TArgs extends unknown[] = unknown[]> {
|
|
138
|
+
prototype: T
|
|
139
|
+
new (...args: TArgs): T
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* This helper creates a partial interface with exception to the defined key values.
|
|
144
|
+
*/
|
|
145
|
+
type PartialExcept<TOject, TKey extends keyof TOject> = Partial<Omit<TOject, TKey>> & Pick<TOject, TKey>
|
|
146
|
+
|
|
147
|
+
type EventListener<TEvent extends keyof ExtensionServer.InboundEvents> = (
|
|
148
|
+
payload: ExtensionServer.InboundEvents[TEvent],
|
|
149
|
+
) => void
|
|
150
|
+
|
|
151
|
+
type EventUnsubscriber = () => void
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const AVAILABLE_SURFACES = ['admin', 'checkout', 'post_purchase', 'point_of_sale', 'customer-accounts'] as const
|
|
156
|
+
|
|
157
|
+
export type Surface = (typeof AVAILABLE_SURFACES)[number]
|
|
158
|
+
|
|
159
|
+
export {}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import {ExtensionServerProvider} from './ExtensionServerProvider'
|
|
2
|
+
import {mockApp, mockExtension} from '../testing'
|
|
3
|
+
import {useExtensionServerContext} from '../hooks'
|
|
4
|
+
import {createConnectedAction} from '../state'
|
|
5
|
+
import WS from 'jest-websocket-mock'
|
|
6
|
+
import {renderHook, withProviders} from '@shopify/ui-extensions-test-utils'
|
|
7
|
+
|
|
8
|
+
describe('ExtensionServerProvider tests', () => {
|
|
9
|
+
describe('client tests', () => {
|
|
10
|
+
test('creates a new ExtensionServerClient instance', async () => {
|
|
11
|
+
const options = {connection: {url: 'ws://example-host.com:8000/extensions/'}}
|
|
12
|
+
|
|
13
|
+
const wrapper = renderHook(useExtensionServerContext, withProviders(ExtensionServerProvider), {options})
|
|
14
|
+
|
|
15
|
+
expect(wrapper.result.client).toBeDefined()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('does not start a new connection if an empty url is passed', async () => {
|
|
19
|
+
const options = {connection: {}}
|
|
20
|
+
|
|
21
|
+
const wrapper = renderHook(useExtensionServerContext, withProviders(ExtensionServerProvider), {options})
|
|
22
|
+
|
|
23
|
+
expect(wrapper.result.client.connection).toBeUndefined()
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
describe('connect tests', () => {
|
|
28
|
+
test('starts a new connection by calling connect', async () => {
|
|
29
|
+
const options = {connection: {url: 'ws://example-host.com:8000/extensions/'}}
|
|
30
|
+
const socket = new WS(options.connection.url, {jsonProtocol: true})
|
|
31
|
+
const wrapper = renderHook(useExtensionServerContext, withProviders(ExtensionServerProvider), {
|
|
32
|
+
options: {
|
|
33
|
+
connection: {url: ''},
|
|
34
|
+
},
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
expect(socket.server.clients()).toHaveLength(0)
|
|
38
|
+
|
|
39
|
+
wrapper.act(({connect}) => connect(options))
|
|
40
|
+
|
|
41
|
+
expect(wrapper.result.client.connection).toBeDefined()
|
|
42
|
+
expect(socket.server.clients()).toHaveLength(1)
|
|
43
|
+
socket.close()
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
describe('dispatch tests', () => {
|
|
48
|
+
test('updates the state', async () => {
|
|
49
|
+
const options = {connection: {url: 'ws://example-host.com:8000/extensions/'}}
|
|
50
|
+
const app = mockApp()
|
|
51
|
+
const extension = mockExtension()
|
|
52
|
+
const payload = {app, extensions: [extension], store: 'test-store.com'}
|
|
53
|
+
const wrapper = renderHook(useExtensionServerContext, withProviders(ExtensionServerProvider), {options})
|
|
54
|
+
|
|
55
|
+
wrapper.act(({dispatch}) => {
|
|
56
|
+
dispatch({type: 'connected', payload})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
expect(wrapper.result.state).toStrictEqual({
|
|
60
|
+
app,
|
|
61
|
+
extensions: [extension],
|
|
62
|
+
store: 'test-store.com',
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
describe('state tests', () => {
|
|
68
|
+
let socket: WS
|
|
69
|
+
const options = {connection: {url: 'ws://example-host.com:8000/extensions/'}}
|
|
70
|
+
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
socket = new WS(options.connection.url, {jsonProtocol: true})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
afterEach(() => {
|
|
76
|
+
socket.close()
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('persists connection data to the state', async () => {
|
|
80
|
+
const app = mockApp()
|
|
81
|
+
const extension = mockExtension()
|
|
82
|
+
const data = {app, store: 'test-store.com', extensions: [extension]}
|
|
83
|
+
const wrapper = renderHook(useExtensionServerContext, withProviders(ExtensionServerProvider), {options})
|
|
84
|
+
|
|
85
|
+
wrapper.act(() => socket.send({event: 'connected', data}))
|
|
86
|
+
|
|
87
|
+
expect(wrapper.result.state).toEqual({
|
|
88
|
+
app,
|
|
89
|
+
extensions: [extension],
|
|
90
|
+
store: 'test-store.com',
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test('persists update data to the state', async () => {
|
|
95
|
+
const app = mockApp()
|
|
96
|
+
const extension = mockExtension()
|
|
97
|
+
const update = {...extension, version: 'v2'}
|
|
98
|
+
const data = {app, store: 'test-store.com', extensions: [extension]}
|
|
99
|
+
const wrapper = renderHook(useExtensionServerContext, withProviders(ExtensionServerProvider), {options})
|
|
100
|
+
|
|
101
|
+
wrapper.act(({dispatch}) => {
|
|
102
|
+
dispatch(createConnectedAction(data))
|
|
103
|
+
|
|
104
|
+
socket.send({event: 'update', data: {...data, extensions: [update]}})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
expect(wrapper.result.state).toEqual({
|
|
108
|
+
app,
|
|
109
|
+
extensions: [update],
|
|
110
|
+
store: 'test-store.com',
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// eslint-disable-next-line vitest/no-disabled-tests
|
|
115
|
+
test.skip('persists refresh data to the state', async () => {
|
|
116
|
+
const app = mockApp()
|
|
117
|
+
const extension = mockExtension()
|
|
118
|
+
const data = {app, store: 'test-store.com', extensions: [extension]}
|
|
119
|
+
const wrapper = renderHook(useExtensionServerContext, withProviders(ExtensionServerProvider), {options})
|
|
120
|
+
|
|
121
|
+
wrapper.act(({dispatch}) => {
|
|
122
|
+
dispatch(createConnectedAction(data))
|
|
123
|
+
|
|
124
|
+
socket.send({
|
|
125
|
+
event: 'dispatch',
|
|
126
|
+
data: {type: 'refresh', payload: [{uuid: extension.uuid}]},
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
const [updatedExtension] = wrapper.result.state.extensions
|
|
131
|
+
expect(updatedExtension.assets.main.url).not.toEqual(extension.assets.main.url)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test('persists focus data to the state', async () => {
|
|
135
|
+
const app = mockApp()
|
|
136
|
+
const extension = mockExtension()
|
|
137
|
+
const data = {app, store: 'test-store.com', extensions: [extension]}
|
|
138
|
+
const wrapper = renderHook(useExtensionServerContext, withProviders(ExtensionServerProvider), {options})
|
|
139
|
+
|
|
140
|
+
wrapper.act(({dispatch}) => {
|
|
141
|
+
dispatch(createConnectedAction(data))
|
|
142
|
+
|
|
143
|
+
socket.send({
|
|
144
|
+
event: 'dispatch',
|
|
145
|
+
data: {type: 'focus', payload: [{uuid: extension.uuid}]},
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
const [updatedExtension] = wrapper.result.state.extensions
|
|
150
|
+
expect(updatedExtension.development.focused).toBe(true)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
test('persists unfocus data to the state', async () => {
|
|
154
|
+
const app = mockApp()
|
|
155
|
+
const extension = mockExtension()
|
|
156
|
+
extension.development.focused = true
|
|
157
|
+
const data = {app, store: 'test-store.com', extensions: [extension]}
|
|
158
|
+
const wrapper = renderHook(useExtensionServerContext, withProviders(ExtensionServerProvider), {options})
|
|
159
|
+
|
|
160
|
+
wrapper.act(({dispatch}) => {
|
|
161
|
+
dispatch(createConnectedAction(data))
|
|
162
|
+
|
|
163
|
+
socket.send({
|
|
164
|
+
event: 'dispatch',
|
|
165
|
+
data: {type: 'unfocus'},
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
const [updatedExtension] = wrapper.result.state.extensions
|
|
170
|
+
expect(updatedExtension.development.focused).toBe(false)
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
})
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {extensionServerContext} from './constants'
|
|
2
|
+
import {
|
|
3
|
+
createConnectedAction,
|
|
4
|
+
createUpdateAction,
|
|
5
|
+
createRefreshAction,
|
|
6
|
+
createFocusAction,
|
|
7
|
+
createUnfocusAction,
|
|
8
|
+
} from '../state'
|
|
9
|
+
|
|
10
|
+
import {ExtensionServerClient} from '../ExtensionServerClient'
|
|
11
|
+
import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect'
|
|
12
|
+
import {useExtensionServerState} from '../hooks/useExtensionServerState'
|
|
13
|
+
import React, {useCallback, useMemo, useState} from 'react'
|
|
14
|
+
|
|
15
|
+
import type {ExtensionServerProviderProps} from './types'
|
|
16
|
+
|
|
17
|
+
export function ExtensionServerProvider({children, options: defaultOptions}: ExtensionServerProviderProps) {
|
|
18
|
+
const [state, dispatch] = useExtensionServerState()
|
|
19
|
+
const [options, setOptions] = useState(defaultOptions)
|
|
20
|
+
const [client] = useState<ExtensionServer.Client>(() => new ExtensionServerClient())
|
|
21
|
+
|
|
22
|
+
const connect = useCallback(
|
|
23
|
+
(newOptions: ExtensionServer.Options = options) => {
|
|
24
|
+
setOptions(newOptions)
|
|
25
|
+
},
|
|
26
|
+
[options],
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
useIsomorphicLayoutEffect(() => client.connect(options), [client, options])
|
|
30
|
+
|
|
31
|
+
useIsomorphicLayoutEffect(() => {
|
|
32
|
+
const listeners = [
|
|
33
|
+
client.on('update', (payload) => dispatch(createUpdateAction(payload))),
|
|
34
|
+
client.on('connected', (payload) => dispatch(createConnectedAction(payload))),
|
|
35
|
+
client.on('refresh', (payload) => dispatch(createRefreshAction(payload))),
|
|
36
|
+
client.on('focus', (payload) => dispatch(createFocusAction(payload))),
|
|
37
|
+
client.on('unfocus', (payload) => dispatch(createUnfocusAction(payload))),
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
return () => listeners.forEach((unsubscribe) => unsubscribe())
|
|
41
|
+
}, [dispatch])
|
|
42
|
+
|
|
43
|
+
const context = useMemo(() => ({dispatch, state, connect, client}), [dispatch, connect, state, client])
|
|
44
|
+
|
|
45
|
+
return <extensionServerContext.Provider value={context}>{children}</extensionServerContext.Provider>
|
|
46
|
+
}
|