@agent-native/dispatch 0.33.0 → 0.33.1-nightly-20260901225823

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.
@@ -1,6 +1,18 @@
1
1
  // @vitest-environment happy-dom
2
2
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
3
3
  import { navigateToWorkspaceApp, shouldOpenWorkspaceAppInTopWindow, workspaceAppDirectHref, workspaceAppEmbedTarget, } from "./workspace-apps.js";
4
+ function setAncestorOrigin(origin) {
5
+ Object.defineProperty(window.location, "ancestorOrigins", {
6
+ configurable: true,
7
+ value: origin
8
+ ? {
9
+ 0: origin,
10
+ length: 1,
11
+ item: (index) => (index === 0 ? origin : null),
12
+ }
13
+ : undefined,
14
+ });
15
+ }
4
16
  describe("workspaceAppEmbedTarget", () => {
5
17
  it("uses the app URL as the embed root when a mount path is also present", () => {
6
18
  expect(workspaceAppEmbedTarget({
@@ -34,9 +46,11 @@ describe("workspaceAppDirectHref", () => {
34
46
  describe("navigateToWorkspaceApp", () => {
35
47
  beforeEach(() => {
36
48
  window.history.replaceState({}, "", "/");
49
+ setAncestorOrigin(null);
37
50
  });
38
51
  afterEach(() => {
39
52
  window.history.replaceState({}, "", "/");
53
+ setAncestorOrigin(null);
40
54
  Object.defineProperty(window, "parent", {
41
55
  configurable: true,
42
56
  value: window,
@@ -53,6 +67,14 @@ describe("navigateToWorkspaceApp", () => {
53
67
  });
54
68
  expect(shouldOpenWorkspaceAppInTopWindow()).toBe(false);
55
69
  });
70
+ it("keeps the hosted Dispatch shell inline", () => {
71
+ Object.defineProperty(window, "parent", {
72
+ configurable: true,
73
+ value: {},
74
+ });
75
+ setAncestorOrigin("https://agent-workspace.builder.io");
76
+ expect(shouldOpenWorkspaceAppInTopWindow()).toBe(false);
77
+ });
56
78
  it("uses the top window when Dispatch is inside a Builder webview", () => {
57
79
  const topWindow = { location: { href: "" } };
58
80
  window.history.replaceState({}, "", "/?builder.preview=interact");
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-apps.test.js","sourceRoot":"","sources":["../../src/lib/workspace-apps.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment happy-dom\nimport { afterEach, beforeEach, describe, expect, it } from \"vitest\";\n\nimport {\n navigateToWorkspaceApp,\n shouldOpenWorkspaceAppInTopWindow,\n workspaceAppDirectHref,\n workspaceAppEmbedTarget,\n} from \"./workspace-apps\";\n\ndescribe(\"workspaceAppEmbedTarget\", () => {\n it(\"uses the app URL as the embed root when a mount path is also present\", () => {\n expect(\n workspaceAppEmbedTarget({\n path: \"/content\",\n url: \"https://workspace.example.test/content\",\n }),\n ).toEqual({ url: \"https://workspace.example.test/content\" });\n });\n\n it(\"falls back to the mounted path when no app URL is available\", () => {\n expect(workspaceAppEmbedTarget({ path: \"/content\", url: null })).toEqual({\n path: \"/content\",\n });\n });\n});\n\ndescribe(\"workspaceAppDirectHref\", () => {\n it(\"joins an app-relative route onto an absolute app URL\", () => {\n expect(\n workspaceAppDirectHref(\n { path: \"/atlas\", url: \"https://workspace.example.test/atlas\" },\n \"/emails?status=failed#latest\",\n ),\n ).toBe(\"https://workspace.example.test/atlas/emails?status=failed#latest\");\n });\n\n it(\"does not duplicate a mount path already present in the target\", () => {\n expect(\n workspaceAppDirectHref(\n { path: \"/atlas\", url: \"https://workspace.example.test/atlas\" },\n \"/atlas\",\n ),\n ).toBe(\"https://workspace.example.test/atlas\");\n });\n\n it(\"resolves a mounted relative app path\", () => {\n expect(workspaceAppDirectHref({ path: \"/atlas\" }, \"/emails\")).toBe(\n \"/atlas/emails\",\n );\n });\n\n it(\"falls back to a safe mount path when the app URL is invalid\", () => {\n expect(\n workspaceAppDirectHref(\n { path: \"/atlas\", url: \"javascript:alert(1)\" },\n \"/\",\n ),\n ).toBe(\"/atlas\");\n });\n\n it(\"rejects an unsafe target path\", () => {\n expect(workspaceAppDirectHref({ path: \"/atlas\" }, \"//evil.example\")).toBe(\n null,\n );\n });\n});\n\ndescribe(\"navigateToWorkspaceApp\", () => {\n beforeEach(() => {\n window.history.replaceState({}, \"\", \"/\");\n });\n\n afterEach(() => {\n window.history.replaceState({}, \"\", \"/\");\n Object.defineProperty(window, \"parent\", {\n configurable: true,\n value: window,\n });\n Object.defineProperty(window, \"top\", {\n configurable: true,\n value: window,\n });\n });\n\n it(\"keeps a normal iframe inline\", () => {\n Object.defineProperty(window, \"parent\", {\n configurable: true,\n value: {},\n });\n\n expect(shouldOpenWorkspaceAppInTopWindow()).toBe(false);\n });\n\n it(\"uses the top window when Dispatch is inside a Builder webview\", () => {\n const topWindow = { location: { href: \"\" } } as unknown as Window;\n window.history.replaceState({}, \"\", \"/?builder.preview=interact\");\n Object.defineProperty(window, \"parent\", {\n configurable: true,\n value: {},\n });\n Object.defineProperty(window, \"top\", {\n configurable: true,\n value: topWindow,\n });\n\n expect(shouldOpenWorkspaceAppInTopWindow()).toBe(true);\n navigateToWorkspaceApp(\"/mail\");\n\n expect(topWindow.location.href).toBe(\n new URL(\"/mail\", window.location.href).href,\n );\n });\n\n it(\"reports when the embedded top window rejects navigation\", () => {\n const location = {} as Location;\n Object.defineProperty(location, \"href\", {\n configurable: true,\n set: () => {\n throw new Error(\"Top navigation is blocked\");\n },\n });\n const topWindow = { location } as unknown as Window;\n window.history.replaceState({}, \"\", \"/?builder.preview=interact\");\n Object.defineProperty(window, \"parent\", {\n configurable: true,\n value: {},\n });\n Object.defineProperty(window, \"top\", {\n configurable: true,\n value: topWindow,\n });\n\n expect(navigateToWorkspaceApp(\"/mail\")).toBe(false);\n });\n\n it(\"rejects non-http navigation targets\", () => {\n const topWindow = { location: { href: \"\" } } as unknown as Window;\n Object.defineProperty(window, \"parent\", {\n configurable: true,\n value: {},\n });\n Object.defineProperty(window, \"top\", {\n configurable: true,\n value: topWindow,\n });\n\n expect(navigateToWorkspaceApp(\"javascript:alert(1)\")).toBe(false);\n expect(topWindow.location.href).toBe(\"\");\n });\n});\n"]}
1
+ {"version":3,"file":"workspace-apps.test.js","sourceRoot":"","sources":["../../src/lib/workspace-apps.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment happy-dom\nimport { afterEach, beforeEach, describe, expect, it } from \"vitest\";\n\nimport {\n navigateToWorkspaceApp,\n shouldOpenWorkspaceAppInTopWindow,\n workspaceAppDirectHref,\n workspaceAppEmbedTarget,\n} from \"./workspace-apps\";\n\nfunction setAncestorOrigin(origin: string | null) {\n Object.defineProperty(window.location, \"ancestorOrigins\", {\n configurable: true,\n value: origin\n ? {\n 0: origin,\n length: 1,\n item: (index: number) => (index === 0 ? origin : null),\n }\n : undefined,\n });\n}\n\ndescribe(\"workspaceAppEmbedTarget\", () => {\n it(\"uses the app URL as the embed root when a mount path is also present\", () => {\n expect(\n workspaceAppEmbedTarget({\n path: \"/content\",\n url: \"https://workspace.example.test/content\",\n }),\n ).toEqual({ url: \"https://workspace.example.test/content\" });\n });\n\n it(\"falls back to the mounted path when no app URL is available\", () => {\n expect(workspaceAppEmbedTarget({ path: \"/content\", url: null })).toEqual({\n path: \"/content\",\n });\n });\n});\n\ndescribe(\"workspaceAppDirectHref\", () => {\n it(\"joins an app-relative route onto an absolute app URL\", () => {\n expect(\n workspaceAppDirectHref(\n { path: \"/atlas\", url: \"https://workspace.example.test/atlas\" },\n \"/emails?status=failed#latest\",\n ),\n ).toBe(\"https://workspace.example.test/atlas/emails?status=failed#latest\");\n });\n\n it(\"does not duplicate a mount path already present in the target\", () => {\n expect(\n workspaceAppDirectHref(\n { path: \"/atlas\", url: \"https://workspace.example.test/atlas\" },\n \"/atlas\",\n ),\n ).toBe(\"https://workspace.example.test/atlas\");\n });\n\n it(\"resolves a mounted relative app path\", () => {\n expect(workspaceAppDirectHref({ path: \"/atlas\" }, \"/emails\")).toBe(\n \"/atlas/emails\",\n );\n });\n\n it(\"falls back to a safe mount path when the app URL is invalid\", () => {\n expect(\n workspaceAppDirectHref(\n { path: \"/atlas\", url: \"javascript:alert(1)\" },\n \"/\",\n ),\n ).toBe(\"/atlas\");\n });\n\n it(\"rejects an unsafe target path\", () => {\n expect(workspaceAppDirectHref({ path: \"/atlas\" }, \"//evil.example\")).toBe(\n null,\n );\n });\n});\n\ndescribe(\"navigateToWorkspaceApp\", () => {\n beforeEach(() => {\n window.history.replaceState({}, \"\", \"/\");\n setAncestorOrigin(null);\n });\n\n afterEach(() => {\n window.history.replaceState({}, \"\", \"/\");\n setAncestorOrigin(null);\n Object.defineProperty(window, \"parent\", {\n configurable: true,\n value: window,\n });\n Object.defineProperty(window, \"top\", {\n configurable: true,\n value: window,\n });\n });\n\n it(\"keeps a normal iframe inline\", () => {\n Object.defineProperty(window, \"parent\", {\n configurable: true,\n value: {},\n });\n\n expect(shouldOpenWorkspaceAppInTopWindow()).toBe(false);\n });\n\n it(\"keeps the hosted Dispatch shell inline\", () => {\n Object.defineProperty(window, \"parent\", {\n configurable: true,\n value: {},\n });\n setAncestorOrigin(\"https://agent-workspace.builder.io\");\n\n expect(shouldOpenWorkspaceAppInTopWindow()).toBe(false);\n });\n\n it(\"uses the top window when Dispatch is inside a Builder webview\", () => {\n const topWindow = { location: { href: \"\" } } as unknown as Window;\n window.history.replaceState({}, \"\", \"/?builder.preview=interact\");\n Object.defineProperty(window, \"parent\", {\n configurable: true,\n value: {},\n });\n Object.defineProperty(window, \"top\", {\n configurable: true,\n value: topWindow,\n });\n\n expect(shouldOpenWorkspaceAppInTopWindow()).toBe(true);\n navigateToWorkspaceApp(\"/mail\");\n\n expect(topWindow.location.href).toBe(\n new URL(\"/mail\", window.location.href).href,\n );\n });\n\n it(\"reports when the embedded top window rejects navigation\", () => {\n const location = {} as Location;\n Object.defineProperty(location, \"href\", {\n configurable: true,\n set: () => {\n throw new Error(\"Top navigation is blocked\");\n },\n });\n const topWindow = { location } as unknown as Window;\n window.history.replaceState({}, \"\", \"/?builder.preview=interact\");\n Object.defineProperty(window, \"parent\", {\n configurable: true,\n value: {},\n });\n Object.defineProperty(window, \"top\", {\n configurable: true,\n value: topWindow,\n });\n\n expect(navigateToWorkspaceApp(\"/mail\")).toBe(false);\n });\n\n it(\"rejects non-http navigation targets\", () => {\n const topWindow = { location: { href: \"\" } } as unknown as Window;\n Object.defineProperty(window, \"parent\", {\n configurable: true,\n value: {},\n });\n Object.defineProperty(window, \"top\", {\n configurable: true,\n value: topWindow,\n });\n\n expect(navigateToWorkspaceApp(\"javascript:alert(1)\")).toBe(false);\n expect(topWindow.location.href).toBe(\"\");\n });\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/dispatch",
3
- "version": "0.33.0",
3
+ "version": "0.33.1-nightly-20260901225823",
4
4
  "description": "Dispatch — workspace control plane for agent-native apps. Vault, integrations, destinations, scheduled jobs, and cross-app delegation, shipped as a single drop-in package.",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -101,10 +101,10 @@
101
101
  "typescript-7": "npm:typescript@^7.0.2",
102
102
  "vite": "8.1.0",
103
103
  "vitest": "^4.1.5",
104
- "@agent-native/core": "0.176.3"
104
+ "@agent-native/core": "0.176.4-nightly-20260901225823"
105
105
  },
106
106
  "peerDependencies": {
107
- "@agent-native/core": ">=0.8.0",
107
+ "@agent-native/core": "0.176.4-nightly-20260901225823",
108
108
  "react": ">=19.2.7",
109
109
  "react-dom": ">=19.2.7",
110
110
  "react-router": ">=8"
@@ -8,6 +8,19 @@ import {
8
8
  workspaceAppEmbedTarget,
9
9
  } from "./workspace-apps";
10
10
 
11
+ function setAncestorOrigin(origin: string | null) {
12
+ Object.defineProperty(window.location, "ancestorOrigins", {
13
+ configurable: true,
14
+ value: origin
15
+ ? {
16
+ 0: origin,
17
+ length: 1,
18
+ item: (index: number) => (index === 0 ? origin : null),
19
+ }
20
+ : undefined,
21
+ });
22
+ }
23
+
11
24
  describe("workspaceAppEmbedTarget", () => {
12
25
  it("uses the app URL as the embed root when a mount path is also present", () => {
13
26
  expect(
@@ -69,10 +82,12 @@ describe("workspaceAppDirectHref", () => {
69
82
  describe("navigateToWorkspaceApp", () => {
70
83
  beforeEach(() => {
71
84
  window.history.replaceState({}, "", "/");
85
+ setAncestorOrigin(null);
72
86
  });
73
87
 
74
88
  afterEach(() => {
75
89
  window.history.replaceState({}, "", "/");
90
+ setAncestorOrigin(null);
76
91
  Object.defineProperty(window, "parent", {
77
92
  configurable: true,
78
93
  value: window,
@@ -92,6 +107,16 @@ describe("navigateToWorkspaceApp", () => {
92
107
  expect(shouldOpenWorkspaceAppInTopWindow()).toBe(false);
93
108
  });
94
109
 
110
+ it("keeps the hosted Dispatch shell inline", () => {
111
+ Object.defineProperty(window, "parent", {
112
+ configurable: true,
113
+ value: {},
114
+ });
115
+ setAncestorOrigin("https://agent-workspace.builder.io");
116
+
117
+ expect(shouldOpenWorkspaceAppInTopWindow()).toBe(false);
118
+ });
119
+
95
120
  it("uses the top window when Dispatch is inside a Builder webview", () => {
96
121
  const topWindow = { location: { href: "" } } as unknown as Window;
97
122
  window.history.replaceState({}, "", "/?builder.preview=interact");