@hummingbirdworks/proxy 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -127,14 +127,70 @@ instructions for your OS/browser.
127
127
  open DevTools → Application → Service Workers and check "Bypass for network"
128
128
  to disable the service worker cache.
129
129
 
130
- ## Config reference (CLI / env)
130
+ ## Using with Vite (HMR)
131
+
132
+ A `devserver` rule routes a web resource's requests to a running Vite dev
133
+ server, giving you hot module reload on the Dynamics-hosted page.
134
+
135
+ The dev server can stay on plain HTTP — the browser only talks to the proxy, and
136
+ the proxy relays requests (including the HMR websocket) to `localhost`. Because
137
+ the Dynamics page is HTTPS, the browser would block an insecure `ws://` HMR
138
+ socket as mixed content, so point the HMR client at `wss` and let the proxy
139
+ forward it to the HTTP dev server. No dev-server cert (or `vite-plugin-mkcert`)
140
+ is needed.
141
+
142
+ ### Vite config
143
+
144
+ The package ships a Vite plugin that helps with the configuration of web resources.
145
+ It sets the base path, configures settings to support HMR with the proxy, and turns
146
+ off cache busting since powerapps already has cache busting when you publish customizations.
147
+
148
+ ```ts
149
+ import { defineConfig } from 'vite'
150
+ import { svelte } from '@sveltejs/vite-plugin-svelte'
151
+ import { powerAppsWebResource } from '@hummingbirdworks/proxy/vite'
152
+
153
+ export default defineConfig({
154
+ plugins: [
155
+ svelte(), // or react(), vue(), etc.
156
+ powerAppsWebResource({ prefix: 'test_/myapp/' }),
157
+ ],
158
+ server: { port: 5173 },
159
+ })
160
+ ```
131
161
 
132
- - **Config path resolution:** the CLI uses the first non-flag argument as the
133
- config path, defaulting to `./proxy.config.toml` in the current directory. It
134
- passes the resolved absolute path to the addon via the
135
- `HUMMINGBIRD_PROXY_CONFIG` environment variable.
136
- - **Running the addon directly** (without the CLI) is also supported:
162
+ Options:
137
163
 
138
- ```sh
139
- HUMMINGBIRD_PROXY_CONFIG=/abs/path/proxy.config.toml mitmdump -s powerapp_dev_proxy.py
140
- ```
164
+ - `prefix` (required): the web resource path, e.g. `test_/myapp/`.
165
+ - `hmrClientPort` (default `443`): port the browser uses for the HMR socket.
166
+ - `stableFilenames` (default `true`): emit unhashed filenames and a single
167
+ stylesheet. Set `false` to keep Vite's defaults.
168
+
169
+ If your HTML entry isn't `index.html`, add it to `build.rollupOptions.input`.
170
+
171
+ ### Proxy rule
172
+
173
+ Point a `devserver` rule at the dev server. The `url` scheme/port must match
174
+ what Vite serves (`http://localhost:5173` by default here):
175
+
176
+ ```toml
177
+ [[rules]]
178
+ type = "devserver"
179
+ name = "test_/myapp/"
180
+ url = "http://localhost:5173"
181
+ domain = "myorg.crm.dynamics.com"
182
+ ```
183
+
184
+ ### Run
185
+
186
+ Start the dev server and the proxy, then browse through the proxy:
187
+
188
+ ```sh
189
+ npm run dev
190
+ npm run proxy
191
+ ```
192
+
193
+ Open the Dynamics page hosting the web resource. Edit → save → the page updates
194
+ without a manual refresh. If HMR doesn't trigger, confirm Vite's port matches
195
+ the rule `url`, that the HMR socket connects over `wss`, and that the service
196
+ worker cache is bypassed (see Notes above).
package/package.json CHANGED
@@ -1,12 +1,20 @@
1
1
  {
2
2
  "name": "@hummingbirdworks/proxy",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "mitmproxy-based dev proxy that redirects Dataverse / Dynamics 365 web resources and PCF control assets to local dev builds or a running dev server.",
5
5
  "bin": {
6
6
  "hummingbird-proxy": "bin/hummingbird-proxy.js"
7
7
  },
8
+ "exports": {
9
+ "./vite": {
10
+ "types": "./vite/index.d.ts",
11
+ "default": "./vite/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
8
15
  "files": [
9
16
  "bin/",
17
+ "vite/",
10
18
  "powerapp_dev_proxy.py",
11
19
  "proxy.config.example.toml",
12
20
  "README.md"
@@ -14,6 +22,14 @@
14
22
  "engines": {
15
23
  "node": ">=16"
16
24
  },
25
+ "peerDependencies": {
26
+ "vite": ">=4"
27
+ },
28
+ "peerDependenciesMeta": {
29
+ "vite": {
30
+ "optional": true
31
+ }
32
+ },
17
33
  "keywords": [
18
34
  "mitmproxy",
19
35
  "powerapps",
@@ -184,7 +184,12 @@ def _web_resource_name(request: http.Request) -> str | None:
184
184
  components = request.path_components
185
185
  for i, component in enumerate(components):
186
186
  if component.lower() == "webresources":
187
- return "/".join(components[i + 1:])
187
+ name = "/".join(components[i + 1:])
188
+ # path_components drops a trailing slash; keep it so dev-server root
189
+ # requests (e.g. the Vite HMR base URL) still match a rule prefix.
190
+ if name and request.path.split("?", 1)[0].endswith("/"):
191
+ name += "/"
192
+ return name
188
193
  return None
189
194
 
190
195
 
@@ -0,0 +1,22 @@
1
+ import type { Plugin } from "vite";
2
+
3
+ export interface WebResourceOptions {
4
+ /** Web resource path prefix, e.g. `test_/myapp/`. Leading/trailing slashes are optional. */
5
+ prefix: string;
6
+ /** Port the browser uses for the HMR websocket. Defaults to `443`. */
7
+ hmrClientPort?: number;
8
+ /**
9
+ * Emit unhashed filenames and a single stylesheet so solution components stay
10
+ * stable across builds. Defaults to `true`; set `false` to keep Vite's defaults.
11
+ */
12
+ stableFilenames?: boolean;
13
+ }
14
+
15
+ /**
16
+ * Vite plugin that aligns the dev server and build output with a Dataverse /
17
+ * Dynamics 365 web resource served through `@hummingbirdworks/proxy`.
18
+ *
19
+ * Sets `base` (absolute in dev, relative in build) and a `wss` HMR socket, and
20
+ * optionally emits stable filenames.
21
+ */
22
+ export function powerAppsWebResource(options: WebResourceOptions): Plugin;
package/vite/index.js ADDED
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+
3
+ // Vite plugin that configures a project so its dev server and build output line
4
+ // up with a Dataverse / Dynamics 365 web resource served through the proxy.
5
+
6
+ function powerAppsWebResource(options) {
7
+ if (!options || typeof options.prefix !== "string") {
8
+ throw new Error(
9
+ "powerAppsWebResource: 'prefix' is required, e.g. 'test_/myapp/'."
10
+ );
11
+ }
12
+ const prefix = options.prefix.replace(/^\/+|\/+$/g, "");
13
+ if (!prefix) {
14
+ throw new Error(
15
+ "powerAppsWebResource: 'prefix' must not be empty, e.g. 'test_/myapp/'."
16
+ );
17
+ }
18
+ const clientPort = options.hmrClientPort != null ? options.hmrClientPort : 443;
19
+ const stableFilenames = options.stableFilenames !== false;
20
+
21
+ return {
22
+ name: "@hummingbirdworks/proxy:webresource",
23
+ config(_config, env) {
24
+ const isServe = env.command === "serve";
25
+ // Dynamics pages are HTTPS, so the HMR socket must be wss to avoid
26
+ // mixed-content blocking; the proxy relays it to the HTTP dev server.
27
+ const config = {
28
+ base: isServe ? "/webresources/" + prefix + "/" : "./",
29
+ server: { hmr: { protocol: "wss", clientPort: clientPort } },
30
+ };
31
+ if (stableFilenames) {
32
+ config.build = {
33
+ cssCodeSplit: false,
34
+ rollupOptions: {
35
+ output: {
36
+ // Stable filenames — Dynamics does its own cache busting on publish.
37
+ entryFileNames: "[name].js",
38
+ chunkFileNames: "[name].js",
39
+ assetFileNames: "[name].[ext]",
40
+ },
41
+ },
42
+ };
43
+ }
44
+ return config;
45
+ },
46
+ };
47
+ }
48
+
49
+ exports.powerAppsWebResource = powerAppsWebResource;