diff --git a/lib/browser/polyfills.ts b/lib/browser/polyfills.ts
index b24fa343d..3a2e6b82c 100644
--- a/lib/browser/polyfills.ts
+++ b/lib/browser/polyfills.ts
@@ -2,16 +2,18 @@
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
-
import buffer from 'buffer';
import process from 'process';
// Workaround to get mqtt-js working with Webpack 5
-if (window) {
- (window as any).Buffer = buffer.Buffer;
- (window as any).process = process;
- // NodeJS global shim workaround for Angular
- (window as any).global = window;
+if (typeof self !== 'undefined') {
+ (self as any).Buffer = buffer.Buffer;
+ (self as any).process = process;
+
+ if (self.window) {
+ // NodeJS global shim workaround for Angular
+ (self.window as any).global = window
+ }
}
export {};
diff --git a/samples/browser/service_worker/.eslintrc.cjs b/samples/browser/service_worker/.eslintrc.cjs
new file mode 100644
index 000000000..e7a71d1b2
--- /dev/null
+++ b/samples/browser/service_worker/.eslintrc.cjs
@@ -0,0 +1,18 @@
+module.exports = {
+ root: true,
+ env: { browser: true, es2020: true },
+ extends: [
+ 'eslint:recommended',
+ 'plugin:@typescript-eslint/recommended',
+ 'plugin:react-hooks/recommended',
+ ],
+ ignorePatterns: ['dist','.eslintrc.cjs'],
+ parser: '@typescript-eslint/parser',
+ plugins: ['react-refresh'],
+ rules: {
+ 'react-refresh/only-export-components': [
+ 'warn',
+ { allowConstantExport: true },
+ ],
+ },
+}
diff --git a/samples/browser/service_worker/README.md b/samples/browser/service_worker/README.md
new file mode 100644
index 000000000..e5771fe25
--- /dev/null
+++ b/samples/browser/service_worker/README.md
@@ -0,0 +1,19 @@
+# Service Worker
+
+The sample is a quick demonstration for setup service worker with aws-crt library. The sample would setup an mqtt5 client on service worker installed, and publish a message on service worker "message" event.
+The sample would not be maintained until aws-crt claim full support for service worker.
+
+## Setup
+0. Install SDK, then change folder to `./samples/browser/service_worker`
+1. run `yarn`
+2. run `yarn dev`, open another terminal and run `yarn vite -c vite.config.sw.ts build --watch` which will build the service worker in watch mode
+3. Load [localhost:3030](http://localhost:3030)
+4. Open browser's DevTools/console to track client messages
+
+### Important Note On Testing Service Workers
+ServiceWorkers are designed to live for a long time and be available offline. As such, the caching policies around them are very aggressive, by design. To help with development it is highly recommended to enable "Update on reload" in Chrome dev tools.
+
+1. Open DevTools
+2. Navigate to the _Application_ tab
+3. On the left navigation within Application click _Service workers_
+4. Toggle "Update on reload"
diff --git a/samples/browser/service_worker/index.html b/samples/browser/service_worker/index.html
new file mode 100644
index 000000000..3ff7e3844
--- /dev/null
+++ b/samples/browser/service_worker/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ IOT SDK -- Service Worker
+
+
+
+
+
+
diff --git a/samples/browser/service_worker/package.json b/samples/browser/service_worker/package.json
new file mode 100644
index 000000000..c28348da6
--- /dev/null
+++ b/samples/browser/service_worker/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "iotserviceworker",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "aws-crt": "file:../../../",
+ "events": "^3.3.0",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ },
+ "devDependencies": {
+ "@types/react": "^18.2.64",
+ "@types/react-dom": "^18.2.21",
+ "@typescript-eslint/eslint-plugin": "^7.1.1",
+ "@typescript-eslint/parser": "^7.1.1",
+ "@vitejs/plugin-react": "^4.2.1",
+ "eslint": "^8.57.0",
+ "eslint-plugin-react-hooks": "^4.6.0",
+ "eslint-plugin-react-refresh": "^0.4.5",
+ "typescript": "^5.2.2",
+ "vite": "^5.1.6"
+ }
+}
diff --git a/samples/browser/service_worker/src/App.tsx b/samples/browser/service_worker/src/App.tsx
new file mode 100644
index 000000000..bdc9dd03a
--- /dev/null
+++ b/samples/browser/service_worker/src/App.tsx
@@ -0,0 +1,39 @@
+import { useEffect } from "react";
+
+function App() {
+ useEffect(() => {
+ let registration: ServiceWorkerRegistration | undefined;
+ async function registerServiceWorker() {
+ // Register service worker
+ navigator.serviceWorker.register("./service-worker.js");
+
+ // Get registration
+ registration = await navigator.serviceWorker.ready;
+ const worker = registration.active;
+ if (worker) {
+ worker.postMessage(
+ "This message will trigger the 'message' in the service worker"
+ );
+ }
+ }
+ // Clean up the service worker before register the new one
+ navigator.serviceWorker.getRegistrations().then(registrations => {
+ for (const registration of registrations) {
+ registration.unregister();
+ }
+ });
+ registerServiceWorker();
+
+ return () => {
+ registration?.unregister();
+ };
+ }, []);
+
+ return (
+ <>
+ Please checkout the "Developer Tools" for console messages.
+ >
+ );
+}
+
+export default App;
diff --git a/samples/browser/service_worker/src/config.ts b/samples/browser/service_worker/src/config.ts
new file mode 100644
index 000000000..be440f6fd
--- /dev/null
+++ b/samples/browser/service_worker/src/config.ts
@@ -0,0 +1,4 @@
+export const AUTHORIZER_NAME = "'";
+export const USERNAME = '';
+export const PASSWORD = '';
+export const AWS_IOT_ENDPOINT = '';
diff --git a/samples/browser/service_worker/src/main.tsx b/samples/browser/service_worker/src/main.tsx
new file mode 100644
index 000000000..95e2bdc2c
--- /dev/null
+++ b/samples/browser/service_worker/src/main.tsx
@@ -0,0 +1,9 @@
+import React from "react";
+import ReactDOM from "react-dom/client";
+import App from "./App.tsx";
+
+ReactDOM.createRoot(document.getElementById("root")!).render(
+
+
+
+);
diff --git a/samples/browser/service_worker/src/pub_sub.ts b/samples/browser/service_worker/src/pub_sub.ts
new file mode 100644
index 000000000..8f9502a18
--- /dev/null
+++ b/samples/browser/service_worker/src/pub_sub.ts
@@ -0,0 +1,77 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+import {mqtt5, auth, iot, MqttConnectCustomAuthConfig} from "aws-crt"
+import {once} from "events"
+import * as settings from "./config"
+import { toUtf8 } from '@aws-sdk/util-utf8-browser';
+/// @ts-ignore
+
+let client : mqtt5.Mqtt5Client | undefined = undefined
+const test_topic = "hello/world/qos0"
+
+function createClient() : mqtt5.Mqtt5Client {
+
+ let customAuthConfig : MqttConnectCustomAuthConfig = {
+ authorizerName: settings.AUTHORIZER_NAME,
+ username: settings.USERNAME,
+ password: settings.PASSWORD
+ };
+
+ let builder = iot.AwsIotMqtt5ClientConfigBuilder.newWebsocketMqttBuilderWithCustomAuth(
+ settings.AWS_IOT_ENDPOINT,
+ customAuthConfig
+ );
+
+ console.log("Connecting custom authorizer...");
+ client = new mqtt5.Mqtt5Client(builder.build());
+ client.on("messageReceived",(eventData: mqtt5.MessageReceivedEvent) : void => {
+ console.log("Message Received event: " + JSON.stringify(eventData.message));
+ if (eventData.message.payload) {
+ console.log(" with payload: " + toUtf8(new Uint8Array(eventData.message.payload as ArrayBuffer)));
+ }
+ } );
+
+ return client;
+}
+
+export async function setupConnection() {
+
+ if(client != undefined) return;
+ /** Set up the credentialsProvider */
+ client = createClient();
+
+ const attemptingConnect = once(client, "attemptingConnect");
+ const connectionSuccess = once(client, "connectionSuccess");
+
+ client.start();
+
+ await attemptingConnect;
+ await connectionSuccess;
+
+ const suback = await client.subscribe({
+ subscriptions: [
+ { qos: mqtt5.QoS.AtLeastOnce, topicFilter: test_topic }
+ ]
+ });
+ console.log('Suback result: ' + JSON.stringify(suback));
+}
+
+export async function Mqtt5ClientPublish()
+{
+ await setupConnection()
+ if (!client)
+ {
+ console.log("[Warning] Client has not been setup.")
+ return
+ }
+ const qos0PublishResult = await client.publish({
+ qos: mqtt5.QoS.AtLeastOnce,
+ topicName: test_topic,
+ payload: "This is a qos 1 payload"
+ });
+ console.log('QoS 1 Publish result: ' + JSON.stringify(qos0PublishResult));
+}
+
diff --git a/samples/browser/service_worker/src/service-worker.ts b/samples/browser/service_worker/src/service-worker.ts
new file mode 100644
index 000000000..f2f1380cb
--- /dev/null
+++ b/samples/browser/service_worker/src/service-worker.ts
@@ -0,0 +1,20 @@
+///
+///
+///
+
+import { mqtt} from "aws-crt";
+import { setupConnection, Mqtt5ClientPublish } from "./pub_sub";
+
+addEventListener("install", async (event) => {
+ console.log(`Service Worker Install: ${event.data}`);
+ console.log(`Setup mqtt client`)
+ await setupConnection()
+});
+
+addEventListener("message", async (event) => {
+ console.log(`Message Received: ${event.data}`);
+ await Mqtt5ClientPublish()
+ console.log("Finish Publish Message")
+});
+
+
diff --git a/samples/browser/service_worker/src/vite-env.d.ts b/samples/browser/service_worker/src/vite-env.d.ts
new file mode 100644
index 000000000..11f02fe2a
--- /dev/null
+++ b/samples/browser/service_worker/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/samples/browser/service_worker/tsconfig.json b/samples/browser/service_worker/tsconfig.json
new file mode 100644
index 000000000..c8e2601dc
--- /dev/null
+++ b/samples/browser/service_worker/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "paths": {
+ "aws-crt": ["./node_modules/aws-crt/dist.browser/browser"]
+ },
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src"],
+ "references": [{ "path": "./tsconfig.node.json" }]
+}
diff --git a/samples/browser/service_worker/tsconfig.node.json b/samples/browser/service_worker/tsconfig.node.json
new file mode 100644
index 000000000..97ede7ee6
--- /dev/null
+++ b/samples/browser/service_worker/tsconfig.node.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "skipLibCheck": true,
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "allowSyntheticDefaultImports": true,
+ "strict": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/samples/browser/service_worker/vite.config.sw.ts b/samples/browser/service_worker/vite.config.sw.ts
new file mode 100644
index 000000000..93edfb50a
--- /dev/null
+++ b/samples/browser/service_worker/vite.config.sw.ts
@@ -0,0 +1,15 @@
+import { defineConfig } from 'vite'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ build: {
+ outDir: 'public',
+ minify: false,
+ lib: {
+ entry: 'src/service-worker.ts',
+ name: "service-worker",
+ formats: ["es"],
+ fileName: () => "service-worker.js"
+ }
+ }
+})
diff --git a/samples/browser/service_worker/vite.config.ts b/samples/browser/service_worker/vite.config.ts
new file mode 100644
index 000000000..f0bc9048a
--- /dev/null
+++ b/samples/browser/service_worker/vite.config.ts
@@ -0,0 +1,10 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ server: {
+ port: 3030,
+ },
+ plugins: [react()],
+})