@osdk/widget.vite-plugin 3.3.0-beta.3 → 3.3.0-beta.5
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 +19 -0
- package/build/esm/client/app.js +22 -6
- package/build/esm/client/app.js.map +1 -1
- package/build/esm/common/__tests__/extractWidgetConfig.test.js +3 -3
- package/build/esm/common/__tests__/extractWidgetConfig.test.js.map +1 -1
- package/build/esm/common/extractWidgetConfig.js +4 -1
- package/build/esm/common/extractWidgetConfig.js.map +1 -1
- package/build/esm/common/standardizeFileExtension.js +7 -1
- package/build/esm/common/standardizeFileExtension.js.map +1 -1
- package/build/esm/dev-plugin/FoundryWidgetDevPlugin.js +23 -2
- package/build/esm/dev-plugin/FoundryWidgetDevPlugin.js.map +1 -1
- package/build/esm/dev-plugin/publishDevModeSettings.js +19 -3
- package/build/esm/dev-plugin/publishDevModeSettings.js.map +1 -1
- package/build/site/assets/{allPaths-C9RDB26n.js → allPaths-DAStXfot.js} +1 -1
- package/build/site/assets/{allPathsLoader-DXy-gD-r.js → allPathsLoader-Xesz75gG.js} +2 -2
- package/build/site/assets/{index-D9t4xczX.js → index-C5U_5Xge.js} +10 -10
- package/build/site/assets/{index-CxjWve_y.css → index-DjCw4U9Z.css} +1 -1
- package/build/site/assets/{splitPathsBySizeLoader-CEcIKoAH.js → splitPathsBySizeLoader-DLaQMX-t.js} +1 -1
- package/build/site/index.html +2 -2
- package/build/types/client/app.d.ts.map +1 -1
- package/build/types/common/standardizeFileExtension.d.ts +2 -1
- package/build/types/common/standardizeFileExtension.d.ts.map +1 -1
- package/build/types/dev-plugin/publishDevModeSettings.d.ts.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @osdk/widget.vite-plugin
|
|
2
2
|
|
|
3
|
+
## 3.3.0-beta.5
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1940934: Support extension-less widget config import and improve reporting when config files not found
|
|
8
|
+
- cc33fec: improve dev mode error surfacing
|
|
9
|
+
- 54e674e: Fix widget dev mode with tailwind causing project imports
|
|
10
|
+
- edb062f: Propagate validation error cause in message for widget config validation
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- @osdk/widget.api@3.3.0-beta.5
|
|
15
|
+
|
|
16
|
+
## 3.3.0-beta.4
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- @osdk/widget.api@3.3.0-beta.4
|
|
21
|
+
|
|
3
22
|
## 3.3.0-beta.3
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/build/esm/client/app.js
CHANGED
|
@@ -14,16 +14,23 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { NonIdealState, Spinner, SpinnerSize } from "@blueprintjs/core";
|
|
17
|
+
import { NonIdealState, Pre, Spinner, SpinnerSize } from "@blueprintjs/core";
|
|
18
18
|
import React, { useEffect } from "react";
|
|
19
19
|
import { EntrypointIframe } from "./entrypointIframe.js";
|
|
20
20
|
const POLLING_INTERVAL = 250;
|
|
21
21
|
const REDIRECT_DELAY = 500;
|
|
22
|
+
class ResponseError extends Error {
|
|
23
|
+
constructor(message, response) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.response = response;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
22
28
|
export const App = () => {
|
|
23
29
|
const [entrypointPaths, setEntrypointPaths] = React.useState([]);
|
|
24
30
|
const [pageState, setPageState] = React.useState({
|
|
25
31
|
state: "loading"
|
|
26
32
|
});
|
|
33
|
+
const numAttempts = React.useRef(0);
|
|
27
34
|
|
|
28
35
|
// Load entrypoints values on mount
|
|
29
36
|
useEffect(() => {
|
|
@@ -33,11 +40,15 @@ export const App = () => {
|
|
|
33
40
|
// Poll the finish endpoint until it returns a success or error
|
|
34
41
|
useEffect(() => {
|
|
35
42
|
const poll = window.setInterval(() => {
|
|
36
|
-
void finish().then(result => {
|
|
43
|
+
void finish(numAttempts.current).then(result => {
|
|
37
44
|
if (result.status === "pending") {
|
|
45
|
+
numAttempts.current++;
|
|
38
46
|
return;
|
|
39
47
|
}
|
|
40
48
|
if (result.status === "error") {
|
|
49
|
+
if (result.response != null) {
|
|
50
|
+
throw new ResponseError(result.error, result.response);
|
|
51
|
+
}
|
|
41
52
|
throw new Error(result.error);
|
|
42
53
|
}
|
|
43
54
|
|
|
@@ -60,7 +71,8 @@ export const App = () => {
|
|
|
60
71
|
console.error("Failed to finish dev mode setup:", error);
|
|
61
72
|
setPageState({
|
|
62
73
|
state: "failed",
|
|
63
|
-
error: error instanceof Error ? error.message : undefined
|
|
74
|
+
error: error instanceof Error ? error.message : undefined,
|
|
75
|
+
response: error instanceof ResponseError ? error.response : undefined
|
|
64
76
|
});
|
|
65
77
|
});
|
|
66
78
|
}, POLLING_INTERVAL);
|
|
@@ -85,7 +97,11 @@ export const App = () => {
|
|
|
85
97
|
}), pageState.state === "failed" && /*#__PURE__*/React.createElement(NonIdealState, {
|
|
86
98
|
title: "Failed to start dev mode",
|
|
87
99
|
icon: "error",
|
|
88
|
-
description: pageState.
|
|
100
|
+
description: /*#__PURE__*/React.createElement(React.Fragment, null, pageState.response != null && /*#__PURE__*/React.createElement(Pre, {
|
|
101
|
+
className: "response-block"
|
|
102
|
+
}, pageState.response), /*#__PURE__*/React.createElement(Pre, {
|
|
103
|
+
className: "response-block"
|
|
104
|
+
}, pageState.error))
|
|
89
105
|
}), entrypointPaths.map(entrypointPath => /*#__PURE__*/React.createElement(EntrypointIframe, {
|
|
90
106
|
src: entrypointPath,
|
|
91
107
|
key: entrypointPath
|
|
@@ -94,7 +110,7 @@ export const App = () => {
|
|
|
94
110
|
function loadEntrypoints() {
|
|
95
111
|
return fetch("../entrypoints").then(res => res.json());
|
|
96
112
|
}
|
|
97
|
-
function finish() {
|
|
98
|
-
return fetch(
|
|
113
|
+
function finish(attempt) {
|
|
114
|
+
return fetch(`../finish?attempt=${attempt}`).then(res => res.json());
|
|
99
115
|
}
|
|
100
116
|
//# sourceMappingURL=app.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","names":["NonIdealState","Spinner","SpinnerSize","React","useEffect","EntrypointIframe","POLLING_INTERVAL","REDIRECT_DELAY","App","entrypointPaths","setEntrypointPaths","useState","pageState","setPageState","state","loadEntrypoints","then","poll","window","setInterval","finish","
|
|
1
|
+
{"version":3,"file":"app.js","names":["NonIdealState","Pre","Spinner","SpinnerSize","React","useEffect","EntrypointIframe","POLLING_INTERVAL","REDIRECT_DELAY","ResponseError","Error","constructor","message","response","App","entrypointPaths","setEntrypointPaths","useState","pageState","setPageState","state","numAttempts","useRef","loadEntrypoints","then","poll","window","setInterval","finish","current","result","status","error","clearInterval","isRedirecting","redirectUrl","setTimeout","location","href","catch","console","undefined","createElement","className","title","icon","intent","description","size","SMALL","Fragment","map","entrypointPath","src","key","fetch","res","json","attempt"],"sources":["app.tsx"],"sourcesContent":["/*\n * Copyright 2024 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { NonIdealState, Pre, Spinner, SpinnerSize } from \"@blueprintjs/core\";\nimport React, { useEffect } from \"react\";\nimport { EntrypointIframe } from \"./entrypointIframe.js\";\n\ntype PageState =\n | {\n state: \"loading\";\n }\n | {\n state: \"failed\";\n error?: string;\n response?: string;\n }\n | {\n state: \"success\";\n isRedirecting: boolean;\n };\n\nconst POLLING_INTERVAL = 250;\nconst REDIRECT_DELAY = 500;\n\nclass ResponseError extends Error {\n public readonly response: string;\n\n constructor(message: string, response: string) {\n super(message);\n this.response = response;\n }\n}\n\nexport const App: React.FC = () => {\n const [entrypointPaths, setEntrypointPaths] = React.useState<string[]>([]);\n const [pageState, setPageState] = React.useState<PageState>({\n state: \"loading\",\n });\n const numAttempts = React.useRef(0);\n\n // Load entrypoints values on mount\n useEffect(() => {\n void loadEntrypoints().then(setEntrypointPaths);\n }, []);\n\n // Poll the finish endpoint until it returns a success or error\n useEffect(() => {\n const poll = window.setInterval(() => {\n void finish(numAttempts.current)\n .then((result) => {\n if (result.status === \"pending\") {\n numAttempts.current++;\n return;\n }\n if (result.status === \"error\") {\n if (result.response != null) {\n throw new ResponseError(result.error, result.response);\n }\n throw new Error(result.error);\n }\n\n // On success, we clear the poll and end the loading state\n window.clearInterval(poll);\n setPageState({\n state: \"success\",\n isRedirecting: result.redirectUrl != null,\n });\n\n // When running in Code Workspaces the parent app will handle the redirect\n if (result.status === \"success\" && result.redirectUrl != null) {\n setTimeout(() => {\n window.location.href = result.redirectUrl!;\n }, REDIRECT_DELAY);\n }\n })\n .catch((error: unknown) => {\n window.clearInterval(poll);\n // eslint-disable-next-line no-console\n console.error(\"Failed to finish dev mode setup:\", error);\n setPageState({\n state: \"failed\",\n error: error instanceof Error ? error.message : undefined,\n response: error instanceof ResponseError\n ? error.response\n : undefined,\n });\n });\n }, POLLING_INTERVAL);\n return () => window.clearInterval(poll);\n }, []);\n\n return (\n <div className=\"body\">\n {pageState.state === \"loading\" && (\n <NonIdealState\n title=\"Generating developer mode manifest…\"\n icon={<Spinner intent=\"primary\" />}\n />\n )}\n {pageState.state === \"success\" && (\n <NonIdealState\n title=\"Started dev mode\"\n icon=\"tick-circle\"\n description={\n <div className=\"description\">\n <Spinner intent=\"primary\" size={SpinnerSize.SMALL} />{\" \"}\n {pageState.isRedirecting\n ? <span>Redirecting you…</span>\n : <span>Loading preview…</span>}\n </div>\n }\n />\n )}\n {pageState.state === \"failed\" && (\n <NonIdealState\n title=\"Failed to start dev mode\"\n icon=\"error\"\n description={\n <>\n {pageState.response != null && (\n <Pre className=\"response-block\">{pageState.response}</Pre>\n )}\n <Pre className=\"response-block\">{pageState.error}</Pre>\n </>\n }\n />\n )}\n {/* To load the entrypoint info, we have to actually load it in the browser to get vite to follow the module graph. Since we know these files will fail, we just load them in iframes set to display: none to trigger the load hook in vite */}\n {entrypointPaths.map((entrypointPath) => (\n <EntrypointIframe src={entrypointPath} key={entrypointPath} />\n ))}\n </div>\n );\n};\n\nfunction loadEntrypoints(): Promise<string[]> {\n return fetch(\"../entrypoints\").then((res) => res.json());\n}\n\nfunction finish(attempt: number): Promise<\n | {\n status: \"success\";\n redirectUrl: string | null;\n }\n | {\n status: \"error\";\n error: string;\n response?: string;\n }\n | {\n status: \"pending\";\n }\n> {\n return fetch(`../finish?attempt=${attempt}`).then((res) => res.json());\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -46,7 +46,7 @@ describe("extractWidgetConfig", () => {
|
|
|
46
46
|
throw new Error(`Widget id "${config.id}" does not match allowed pattern (must be camelCase)`);
|
|
47
47
|
});
|
|
48
48
|
await expect(extractWidgetConfig("/path/to/config.ts", MOCK_SERVER)).rejects.toThrow(expect.objectContaining({
|
|
49
|
-
message: "
|
|
49
|
+
message: "Encountered error: 'Widget id \"Invalid-Id\" does not match allowed pattern (must be camelCase)' while loading widget config from /path/to/config.ts",
|
|
50
50
|
cause: expect.objectContaining({
|
|
51
51
|
message: `Widget id "Invalid-Id" does not match allowed pattern (must be camelCase)`
|
|
52
52
|
})
|
|
@@ -59,7 +59,7 @@ describe("extractWidgetConfig", () => {
|
|
|
59
59
|
}
|
|
60
60
|
});
|
|
61
61
|
await expect(extractWidgetConfig("/path/to/config.ts", MOCK_SERVER)).rejects.toThrow(expect.objectContaining({
|
|
62
|
-
message: "
|
|
62
|
+
message: "Encountered error: 'No default export found in /path/to/config.ts' while loading widget config from /path/to/config.ts",
|
|
63
63
|
cause: expect.objectContaining({
|
|
64
64
|
message: "No default export found in /path/to/config.ts"
|
|
65
65
|
})
|
|
@@ -67,7 +67,7 @@ describe("extractWidgetConfig", () => {
|
|
|
67
67
|
});
|
|
68
68
|
test("throws for invalid module path", async () => {
|
|
69
69
|
vi.mocked(MOCK_SERVER.ssrLoadModule).mockRejectedValue(new Error("Module loading failed"));
|
|
70
|
-
await expect(extractWidgetConfig("/invalid/path/config.ts", MOCK_SERVER)).rejects.toThrow("
|
|
70
|
+
await expect(extractWidgetConfig("/invalid/path/config.ts", MOCK_SERVER)).rejects.toThrow("Encountered error: 'Module loading failed' while loading widget config from /invalid/path/config.ts");
|
|
71
71
|
});
|
|
72
72
|
});
|
|
73
73
|
const MOCK_CONFIG = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractWidgetConfig.test.js","names":["beforeEach","describe","expect","test","vi","extractWidgetConfig","validateWidgetConfigModule","MOCK_SERVER","ssrLoadModule","fn","restoreAllMocks","mocked","mockResolvedValue","default","MOCK_CONFIG","validateSpy","spyOn","result","toEqual","toHaveBeenCalledWith","id","name","type","parameters","mockImplementation","config","Error","rejects","toThrow","objectContaining","message","cause","notDefault","mockRejectedValue","description","paramOne","displayName","paramTwo","events","updateParameters","parameterUpdateIds"],"sources":["extractWidgetConfig.test.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ViteDevServer } from \"vite\";\nimport { beforeEach, describe, expect, test, vi } from \"vitest\";\nimport { extractWidgetConfig } from \"../extractWidgetConfig.js\";\nimport * as validateWidgetConfigModule from \"../validateWidgetConfig.js\";\n\nconst MOCK_SERVER = {\n ssrLoadModule: vi.fn(),\n} as unknown as ViteDevServer;\n\ndescribe(\"extractWidgetConfig\", () => {\n beforeEach(() => {\n vi.restoreAllMocks();\n });\n\n test(\"extracts valid widget configuration\", async () => {\n vi.mocked(MOCK_SERVER.ssrLoadModule).mockResolvedValue({\n default: MOCK_CONFIG,\n });\n\n const validateSpy = vi.spyOn(\n validateWidgetConfigModule,\n \"validateWidgetConfig\",\n );\n\n const result = await extractWidgetConfig(\"/path/to/config.ts\", MOCK_SERVER);\n expect(result).toEqual(MOCK_CONFIG);\n expect(validateSpy).toHaveBeenCalledWith(MOCK_CONFIG);\n });\n\n test(\"throws for invalid widget configuration\", async () => {\n const INVALID_CONFIG = {\n id: \"Invalid-Id\",\n name: \"Test Widget\",\n type: \"workshop\",\n parameters: {},\n };\n\n vi.mocked(MOCK_SERVER.ssrLoadModule).mockResolvedValue({\n default: INVALID_CONFIG,\n });\n\n vi.spyOn(validateWidgetConfigModule, \"validateWidgetConfig\")\n .mockImplementation(config => {\n throw new Error(\n `Widget id \"${config.id}\" does not match allowed pattern (must be camelCase)`,\n );\n });\n\n await expect(extractWidgetConfig(\"/path/to/config.ts\", MOCK_SERVER))\n .rejects.toThrow(\n expect.objectContaining({\n message:
|
|
1
|
+
{"version":3,"file":"extractWidgetConfig.test.js","names":["beforeEach","describe","expect","test","vi","extractWidgetConfig","validateWidgetConfigModule","MOCK_SERVER","ssrLoadModule","fn","restoreAllMocks","mocked","mockResolvedValue","default","MOCK_CONFIG","validateSpy","spyOn","result","toEqual","toHaveBeenCalledWith","id","name","type","parameters","mockImplementation","config","Error","rejects","toThrow","objectContaining","message","cause","notDefault","mockRejectedValue","description","paramOne","displayName","paramTwo","events","updateParameters","parameterUpdateIds"],"sources":["extractWidgetConfig.test.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ViteDevServer } from \"vite\";\nimport { beforeEach, describe, expect, test, vi } from \"vitest\";\nimport { extractWidgetConfig } from \"../extractWidgetConfig.js\";\nimport * as validateWidgetConfigModule from \"../validateWidgetConfig.js\";\n\nconst MOCK_SERVER = {\n ssrLoadModule: vi.fn(),\n} as unknown as ViteDevServer;\n\ndescribe(\"extractWidgetConfig\", () => {\n beforeEach(() => {\n vi.restoreAllMocks();\n });\n\n test(\"extracts valid widget configuration\", async () => {\n vi.mocked(MOCK_SERVER.ssrLoadModule).mockResolvedValue({\n default: MOCK_CONFIG,\n });\n\n const validateSpy = vi.spyOn(\n validateWidgetConfigModule,\n \"validateWidgetConfig\",\n );\n\n const result = await extractWidgetConfig(\"/path/to/config.ts\", MOCK_SERVER);\n expect(result).toEqual(MOCK_CONFIG);\n expect(validateSpy).toHaveBeenCalledWith(MOCK_CONFIG);\n });\n\n test(\"throws for invalid widget configuration\", async () => {\n const INVALID_CONFIG = {\n id: \"Invalid-Id\",\n name: \"Test Widget\",\n type: \"workshop\",\n parameters: {},\n };\n\n vi.mocked(MOCK_SERVER.ssrLoadModule).mockResolvedValue({\n default: INVALID_CONFIG,\n });\n\n vi.spyOn(validateWidgetConfigModule, \"validateWidgetConfig\")\n .mockImplementation(config => {\n throw new Error(\n `Widget id \"${config.id}\" does not match allowed pattern (must be camelCase)`,\n );\n });\n\n await expect(extractWidgetConfig(\"/path/to/config.ts\", MOCK_SERVER))\n .rejects.toThrow(\n expect.objectContaining({\n message:\n \"Encountered error: 'Widget id \\\"Invalid-Id\\\" does not match allowed pattern (must be camelCase)' while loading widget config from /path/to/config.ts\",\n cause: expect.objectContaining({\n message:\n `Widget id \"Invalid-Id\" does not match allowed pattern (must be camelCase)`,\n }),\n }),\n );\n });\n\n test(\"throws for missing default export\", async () => {\n vi.mocked(MOCK_SERVER.ssrLoadModule).mockResolvedValue({\n notDefault: { id: \"test\" },\n });\n\n await expect(extractWidgetConfig(\"/path/to/config.ts\", MOCK_SERVER))\n .rejects.toThrow(\n expect.objectContaining({\n message:\n \"Encountered error: 'No default export found in /path/to/config.ts' while loading widget config from /path/to/config.ts\",\n cause: expect.objectContaining({\n message: \"No default export found in /path/to/config.ts\",\n }),\n }),\n );\n });\n\n test(\"throws for invalid module path\", async () => {\n vi.mocked(MOCK_SERVER.ssrLoadModule).mockRejectedValue(\n new Error(\"Module loading failed\"),\n );\n\n await expect(extractWidgetConfig(\"/invalid/path/config.ts\", MOCK_SERVER))\n .rejects.toThrow(\n \"Encountered error: 'Module loading failed' while loading widget config from /invalid/path/config.ts\",\n );\n });\n});\n\nconst MOCK_CONFIG = {\n id: \"testWidget\",\n name: \"Test Widget\",\n description: \"A test widget\",\n type: \"workshop\",\n parameters: {\n paramOne: {\n displayName: \"Parameter One\",\n type: \"string\",\n },\n paramTwo: {\n displayName: \"Parameter Two\",\n type: \"string\",\n },\n },\n events: {\n updateParameters: {\n displayName: \"Update Parameters\",\n parameterUpdateIds: [\"paramOne\", \"paramTwo\"],\n },\n },\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -22,10 +22,13 @@ export async function extractWidgetConfig(moduleId, server) {
|
|
|
22
22
|
if (config == null) {
|
|
23
23
|
throw new Error(`No default export found in ${moduleId}`);
|
|
24
24
|
}
|
|
25
|
+
if (typeof config !== "object" || config["id"] == null && config["name"] == null) {
|
|
26
|
+
server.config.logger.warn(`Config object does not look like a widget config: ${JSON.stringify(config)}`);
|
|
27
|
+
}
|
|
25
28
|
validateWidgetConfig(config);
|
|
26
29
|
return config;
|
|
27
30
|
} catch (error) {
|
|
28
|
-
throw new Error(`
|
|
31
|
+
throw new Error(`Encountered error: '${error instanceof Error ? error.message : error}' while loading widget config from ${moduleId}`, {
|
|
29
32
|
cause: error
|
|
30
33
|
});
|
|
31
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractWidgetConfig.js","names":["validateWidgetConfig","extractWidgetConfig","moduleId","server","configModule","ssrLoadModule","config","default","Error","error","cause"],"sources":["extractWidgetConfig.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ParameterConfig, WidgetConfig } from \"@osdk/widget.api\";\nimport type { ViteDevServer } from \"vite\";\nimport { validateWidgetConfig } from \"./validateWidgetConfig.js\";\n\nexport async function extractWidgetConfig(\n moduleId: string,\n server: ViteDevServer,\n): Promise<WidgetConfig<ParameterConfig>> {\n try {\n const configModule = await server.ssrLoadModule(moduleId);\n const config = configModule.default;\n\n if (config == null) {\n throw new Error(`No default export found in ${moduleId}`);\n }\n\n validateWidgetConfig(config);\n return config as WidgetConfig<ParameterConfig>;\n } catch (error) {\n throw new Error(`
|
|
1
|
+
{"version":3,"file":"extractWidgetConfig.js","names":["validateWidgetConfig","extractWidgetConfig","moduleId","server","configModule","ssrLoadModule","config","default","Error","logger","warn","JSON","stringify","error","message","cause"],"sources":["extractWidgetConfig.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ParameterConfig, WidgetConfig } from \"@osdk/widget.api\";\nimport type { ViteDevServer } from \"vite\";\nimport { validateWidgetConfig } from \"./validateWidgetConfig.js\";\n\nexport async function extractWidgetConfig(\n moduleId: string,\n server: ViteDevServer,\n): Promise<WidgetConfig<ParameterConfig>> {\n try {\n const configModule = await server.ssrLoadModule(moduleId);\n const config = configModule.default;\n\n if (config == null) {\n throw new Error(`No default export found in ${moduleId}`);\n }\n\n if (\n typeof config !== \"object\"\n || (config[\"id\"] == null && config[\"name\"] == null)\n ) {\n server.config.logger.warn(\n `Config object does not look like a widget config: ${\n JSON.stringify(config)\n }`,\n );\n }\n\n validateWidgetConfig(config);\n return config as WidgetConfig<ParameterConfig>;\n } catch (error) {\n throw new Error(\n `Encountered error: '${(error instanceof Error\n ? error.message\n : error)}' while loading widget config from ${moduleId}`,\n { cause: error },\n );\n }\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA,SAASA,oBAAoB,QAAQ,2BAA2B;AAEhE,OAAO,eAAeC,mBAAmBA,CACvCC,QAAgB,EAChBC,MAAqB,EACmB;EACxC,IAAI;IACF,MAAMC,YAAY,GAAG,MAAMD,MAAM,CAACE,aAAa,CAACH,QAAQ,CAAC;IACzD,MAAMI,MAAM,GAAGF,YAAY,CAACG,OAAO;IAEnC,IAAID,MAAM,IAAI,IAAI,EAAE;MAClB,MAAM,IAAIE,KAAK,CAAC,8BAA8BN,QAAQ,EAAE,CAAC;IAC3D;IAEA,IACE,OAAOI,MAAM,KAAK,QAAQ,IACtBA,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,IAAIA,MAAM,CAAC,MAAM,CAAC,IAAI,IAAK,EACnD;MACAH,MAAM,CAACG,MAAM,CAACG,MAAM,CAACC,IAAI,CACvB,qDACEC,IAAI,CAACC,SAAS,CAACN,MAAM,CAAC,EAE1B,CAAC;IACH;IAEAN,oBAAoB,CAACM,MAAM,CAAC;IAC5B,OAAOA,MAAM;EACf,CAAC,CAAC,OAAOO,KAAK,EAAE;IACd,MAAM,IAAIL,KAAK,CACb,uBAAwBK,KAAK,YAAYL,KAAK,GAC1CK,KAAK,CAACC,OAAO,GACbD,KAAK,sCAAuCX,QAAQ,EAAE,EAC1D;MAAEa,KAAK,EAAEF;IAAM,CACjB,CAAC;EACH;AACF","ignoreList":[]}
|
|
@@ -14,12 +14,18 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { CONFIG_FILE_SUFFIX } from "./constants.js";
|
|
18
|
+
|
|
17
19
|
/**
|
|
18
20
|
* Users may import `.js` files in their code, where the source file is actually a `.ts`, `.tsx`, or
|
|
19
21
|
* `.jsx` file on disk. This standardizes these file extension to `.js` so that we can match imports
|
|
20
|
-
* to source files.
|
|
22
|
+
* to source files. Additionally, extension-less imports may be used depending on the module
|
|
23
|
+
* resolution setting so we append `.js` to config file imports to standardize those as well.
|
|
21
24
|
*/
|
|
22
25
|
export function standardizeFileExtension(file) {
|
|
26
|
+
if (file.endsWith(CONFIG_FILE_SUFFIX)) {
|
|
27
|
+
return file + ".js";
|
|
28
|
+
}
|
|
23
29
|
return file.replace(/\.[jt]sx?$/, ".js");
|
|
24
30
|
}
|
|
25
31
|
//# sourceMappingURL=standardizeFileExtension.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standardizeFileExtension.js","names":["standardizeFileExtension","file","replace"],"sources":["standardizeFileExtension.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Users may import `.js` files in their code, where the source file is actually a `.ts`, `.tsx`, or\n * `.jsx` file on disk. This standardizes these file extension to `.js` so that we can match imports\n * to source files.\n */\nexport function standardizeFileExtension(file: string): string {\n return file.replace(/\\.[jt]sx?$/, \".js\");\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"standardizeFileExtension.js","names":["CONFIG_FILE_SUFFIX","standardizeFileExtension","file","endsWith","replace"],"sources":["standardizeFileExtension.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CONFIG_FILE_SUFFIX } from \"./constants.js\";\n\n/**\n * Users may import `.js` files in their code, where the source file is actually a `.ts`, `.tsx`, or\n * `.jsx` file on disk. This standardizes these file extension to `.js` so that we can match imports\n * to source files. Additionally, extension-less imports may be used depending on the module\n * resolution setting so we append `.js` to config file imports to standardize those as well.\n */\nexport function standardizeFileExtension(file: string): string {\n if (file.endsWith(CONFIG_FILE_SUFFIX)) {\n return file + \".js\";\n }\n return file.replace(/\\.[jt]sx?$/, \".js\");\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,kBAAkB,QAAQ,gBAAgB;;AAEnD;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,wBAAwBA,CAACC,IAAY,EAAU;EAC7D,IAAIA,IAAI,CAACC,QAAQ,CAACH,kBAAkB,CAAC,EAAE;IACrC,OAAOE,IAAI,GAAG,KAAK;EACrB;EACA,OAAOA,IAAI,CAACE,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC;AAC1C","ignoreList":[]}
|
|
@@ -105,11 +105,30 @@ export function FoundryWidgetDevPlugin() {
|
|
|
105
105
|
/**
|
|
106
106
|
* Finish the setup process by setting the widget overrides in Foundry and enabling dev mode.
|
|
107
107
|
*/
|
|
108
|
-
server.middlewares.use(serverPath(server, FINISH_PATH), async (
|
|
108
|
+
server.middlewares.use(serverPath(server, FINISH_PATH), async (req, res) => {
|
|
109
|
+
// Check for too many attempts
|
|
110
|
+
if (req.url == null) {
|
|
111
|
+
throw new Error("Request URL is undefined");
|
|
112
|
+
}
|
|
113
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
114
|
+
const numAttempts = parseInt(url.searchParams.get("attempt") ?? "0");
|
|
115
|
+
if (numAttempts >= 10) {
|
|
116
|
+
const errorMessage = "Timed out waiting for widget config files to be parsed. Are you sure a widget config is imported?";
|
|
117
|
+
server.config.logger.error(errorMessage);
|
|
118
|
+
res.setHeader("Content-Type", "application/json");
|
|
119
|
+
res.statusCode = 500;
|
|
120
|
+
res.end(JSON.stringify({
|
|
121
|
+
status: "error",
|
|
122
|
+
error: errorMessage
|
|
123
|
+
}));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
109
127
|
// Wait for the setup page to trigger the parsing of the config files
|
|
110
128
|
const numEntrypoints = htmlEntrypoints.length;
|
|
111
129
|
const numConfigFiles = Object.keys(configFileToEntrypoint).length;
|
|
112
130
|
if (numConfigFiles < numEntrypoints) {
|
|
131
|
+
server.config.logger.info(`Waiting for widget config files to be parsed, found ${numConfigFiles} config files out of` + ` ${numEntrypoints} HTML entrypoints.`);
|
|
113
132
|
res.setHeader("Content-Type", "application/json");
|
|
114
133
|
res.end(JSON.stringify({
|
|
115
134
|
status: "pending"
|
|
@@ -159,7 +178,9 @@ export function FoundryWidgetDevPlugin() {
|
|
|
159
178
|
}
|
|
160
179
|
|
|
161
180
|
// Look for config files that are imported from an entrypoint file
|
|
162
|
-
|
|
181
|
+
// Also check the config file being imported is in src to avoid picking up imports for other
|
|
182
|
+
// project files like foundry.config.json / eslint.config.mjs when tailwind is used.
|
|
183
|
+
if (standardizedSource.replace(/\.[^/.]+$/, "").endsWith(CONFIG_FILE_SUFFIX) && standardizedSource.includes("/src/") && codeEntrypoints[standardizedImporter] != null) {
|
|
163
184
|
const fullSourcePath = standardizeFileExtension(getFullSourcePath(source, standardizedImporter));
|
|
164
185
|
configFileToEntrypoint[fullSourcePath] = standardizedImporter;
|
|
165
186
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FoundryWidgetDevPlugin.js","names":["path","fileURLToPath","color","sirv","CONFIG_FILE_SUFFIX","DEV_PLUGIN_ID","ENTRYPOINTS_PATH","FINISH_PATH","MODULE_EVALUATION_MODE","SETUP_PATH","VITE_INJECTIONS_PATH","getInputHtmlEntrypoints","standardizeFileExtension","extractInjectedScripts","getBaseHref","getFoundryToken","getWidgetIdOverrideMap","publishDevModeSettings","DIR_DIST","__dirname","dirname","import","meta","url","FoundryWidgetDevPlugin","htmlEntrypoints","codeEntrypoints","configFileToEntrypoint","name","enforce","apply","config","command","mode","process","env","VITEST","buildStart","options","resolvedConfig","configureServer","server","printUrls","printSetupPageUrl","middlewares","use","serverPath","req","res","next","originalUrl","endsWith","statusCode","setHeader","end","resolve","single","dev","_","JSON","stringify","map","entrypoint","numEntrypoints","length","numConfigFiles","Object","keys","status","widgetIdToOverrides","injectedScripts","inlineScripts","join","resolveId","source","importer","standardizedSource","getFullSourcePath","startsWith","cwd","standardizedImporter","normalize","includes","replace","fullSourcePath","subPath","posix","base","setupRoute","logger","info","green","bold"],"sources":["FoundryWidgetDevPlugin.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport color from \"picocolors\";\nimport sirv from \"sirv\";\nimport type { Plugin, ViteDevServer } from \"vite\";\nimport {\n CONFIG_FILE_SUFFIX,\n DEV_PLUGIN_ID,\n ENTRYPOINTS_PATH,\n FINISH_PATH,\n MODULE_EVALUATION_MODE,\n SETUP_PATH,\n VITE_INJECTIONS_PATH,\n} from \"../common/constants.js\";\nimport { getInputHtmlEntrypoints } from \"../common/getInputHtmlEntrypoints.js\";\nimport { standardizeFileExtension } from \"../common/standardizeFileExtension.js\";\nimport { extractInjectedScripts } from \"./extractInjectedScripts.js\";\nimport { getBaseHref } from \"./getBaseHref.js\";\nimport { getFoundryToken } from \"./getFoundryToken.js\";\nimport { getWidgetIdOverrideMap } from \"./getWidgetIdOverrideMap.js\";\nimport { publishDevModeSettings } from \"./publishDevModeSettings.js\";\n\n// Location of the setup page assets\nconst DIR_DIST: string = typeof __dirname !== \"undefined\"\n ? __dirname\n : path.dirname(fileURLToPath(import.meta.url));\n\nexport function FoundryWidgetDevPlugin(): Plugin {\n // The root HTML entrypoints of the build process\n let htmlEntrypoints: string[];\n // Fully resolved paths to the entrypoint files, mapped to relative paths\n const codeEntrypoints: Record<string, string> = {};\n // Store the map of fully resolved config file paths to entrypoint file paths\n const configFileToEntrypoint: Record<string, string> = {};\n\n return {\n name: DEV_PLUGIN_ID,\n enforce: \"pre\",\n // Only apply this plugin during development, skip during tests and build-mode module evaluation\n apply(config, { command }) {\n if (\n config.mode === MODULE_EVALUATION_MODE || process.env.VITEST != null\n ) {\n return false;\n }\n return command === \"serve\";\n },\n\n /**\n * Capture the entrypoints from the Vite config so that we can manually load them on our\n * setup page and trigger module parsing.\n */\n buildStart(options) {\n htmlEntrypoints = getInputHtmlEntrypoints(options);\n },\n\n /**\n * Check for the required token environment variable in dev mode.\n */\n config(resolvedConfig) {\n getFoundryToken(resolvedConfig.mode);\n },\n\n /**\n * Configure the Vite server to serve the setup page and handle the finish endpoint. This\n * endpoint will set the widget overrides in Foundry and enable dev mode.\n */\n configureServer(server) {\n // Override the printUrls function to print the setup page URL\n server.printUrls = () => printSetupPageUrl(server);\n\n /**\n * Redirect `./.palantir/setup` to `./.palantir/setup/` to ensure that relative paths work\n * correctly. Relative paths must be used so that the dev server UI can be accessed on\n * non-root paths.\n */\n server.middlewares.use(\n serverPath(server, SETUP_PATH),\n (req, res, next) => {\n if (req.originalUrl?.endsWith(serverPath(server, SETUP_PATH))) {\n res.statusCode = 301;\n res.setHeader(\"Location\", `${serverPath(server, SETUP_PATH)}/`);\n res.end();\n } else {\n next();\n }\n },\n );\n\n /**\n * Serve the setup page that will load the entrypoints in iframes and trigger the finish\n * endpoint once widgets have been loaded.\n */\n server.middlewares.use(\n serverPath(server, SETUP_PATH),\n sirv(path.resolve(DIR_DIST, \"../../site\"), {\n single: true,\n dev: true,\n }),\n );\n\n /**\n * Make the entrypoints available to the setup page so that it can load them in iframes in\n * order to trigger module parsing.\n */\n server.middlewares.use(\n serverPath(server, ENTRYPOINTS_PATH),\n (_, res) => {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(\n JSON.stringify(\n htmlEntrypoints.map((entrypoint) =>\n serverPath(server, entrypoint)\n ),\n ),\n );\n },\n );\n\n /**\n * Finish the setup process by setting the widget overrides in Foundry and enabling dev mode.\n */\n server.middlewares.use(\n serverPath(server, FINISH_PATH),\n async (_, res) => {\n // Wait for the setup page to trigger the parsing of the config files\n const numEntrypoints = htmlEntrypoints.length;\n const numConfigFiles = Object.keys(configFileToEntrypoint).length;\n if (numConfigFiles < numEntrypoints) {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(JSON.stringify({ status: \"pending\" }));\n return;\n }\n\n // Prepare the widget overrides and finish the setup process\n const widgetIdToOverrides = await getWidgetIdOverrideMap(\n server,\n codeEntrypoints,\n configFileToEntrypoint,\n getBaseHref(server),\n );\n await publishDevModeSettings(\n server,\n widgetIdToOverrides,\n getBaseHref(server),\n res,\n );\n },\n );\n\n /**\n * Serve scripts that would usually be injected into the HTML if Vite had control over the\n * serving of index HTML pages. This is necessary to ensure that plugins like React refresh\n * work correctly.\n */\n server.middlewares.use(\n serverPath(server, VITE_INJECTIONS_PATH),\n async (_, res) => {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Content-Type\", \"application/javascript\");\n const injectedScripts = await extractInjectedScripts(server);\n res.end(injectedScripts.inlineScripts.join(\"\\n\"));\n },\n );\n },\n\n /**\n * As module imports are resolved, we need to capture the entrypoint file paths and the config\n * file paths that are imported from them.\n */\n resolveId(source, importer) {\n if (importer == null) {\n return;\n }\n\n // Standardize the source file extension and get the full path\n const standardizedSource = standardizeFileExtension(\n getFullSourcePath(\n // If the source path is absolute, resolve it against the current working directory\n source.startsWith(\"/\") ? path.join(process.cwd(), source) : source,\n importer,\n ),\n );\n // Importers are already full paths, so just standardize the extension\n // Normalize to ensure consistent path separators on Windows\n const standardizedImporter = standardizeFileExtension(\n path.normalize(importer),\n );\n\n // In dev mode all entrypoints have a generic HTML importer value\n if (\n importer.endsWith(\"index.html\") && !standardizedSource.includes(\"@fs\")\n ) {\n // Store the fully resolved path and the relative path, as we need the former for mapping\n // config files to entrypoints and the latter as a dev mode override script\n codeEntrypoints[standardizedSource] = source;\n }\n\n // Look for config files that are imported from an entrypoint file\n if (\n standardizedSource.replace(/\\.[^/.]+$/, \"\").endsWith(CONFIG_FILE_SUFFIX)\n && codeEntrypoints[standardizedImporter] != null\n ) {\n const fullSourcePath = standardizeFileExtension(\n getFullSourcePath(source, standardizedImporter),\n );\n configFileToEntrypoint[fullSourcePath] = standardizedImporter;\n }\n },\n };\n}\n\n/**\n * During the resolution phase source are given as relative paths to the importer\n */\nfunction getFullSourcePath(source: string, importer: string): string {\n return path.resolve(path.dirname(importer), source);\n}\n\nfunction serverPath(server: ViteDevServer, subPath: string): string {\n // Don't use Windows-style paths when constructing URL paths for the HTTP server\n return path.posix.resolve(server.config.base, subPath);\n}\n\nfunction printSetupPageUrl(server: ViteDevServer) {\n const setupRoute = `${getBaseHref(server)}${SETUP_PATH}/`;\n server.config.logger.info(\n ` ${color.green(\"➜\")} ${\n color.bold(\"Click to enter developer mode for your widget set\")\n }: ${color.green(setupRoute)}`,\n );\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"FoundryWidgetDevPlugin.js","names":["path","fileURLToPath","color","sirv","CONFIG_FILE_SUFFIX","DEV_PLUGIN_ID","ENTRYPOINTS_PATH","FINISH_PATH","MODULE_EVALUATION_MODE","SETUP_PATH","VITE_INJECTIONS_PATH","getInputHtmlEntrypoints","standardizeFileExtension","extractInjectedScripts","getBaseHref","getFoundryToken","getWidgetIdOverrideMap","publishDevModeSettings","DIR_DIST","__dirname","dirname","import","meta","url","FoundryWidgetDevPlugin","htmlEntrypoints","codeEntrypoints","configFileToEntrypoint","name","enforce","apply","config","command","mode","process","env","VITEST","buildStart","options","resolvedConfig","configureServer","server","printUrls","printSetupPageUrl","middlewares","use","serverPath","req","res","next","originalUrl","endsWith","statusCode","setHeader","end","resolve","single","dev","_","JSON","stringify","map","entrypoint","Error","URL","headers","host","numAttempts","parseInt","searchParams","get","errorMessage","logger","error","status","numEntrypoints","length","numConfigFiles","Object","keys","info","widgetIdToOverrides","injectedScripts","inlineScripts","join","resolveId","source","importer","standardizedSource","getFullSourcePath","startsWith","cwd","standardizedImporter","normalize","includes","replace","fullSourcePath","subPath","posix","base","setupRoute","green","bold"],"sources":["FoundryWidgetDevPlugin.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport color from \"picocolors\";\nimport sirv from \"sirv\";\nimport type { Plugin, ViteDevServer } from \"vite\";\nimport {\n CONFIG_FILE_SUFFIX,\n DEV_PLUGIN_ID,\n ENTRYPOINTS_PATH,\n FINISH_PATH,\n MODULE_EVALUATION_MODE,\n SETUP_PATH,\n VITE_INJECTIONS_PATH,\n} from \"../common/constants.js\";\nimport { getInputHtmlEntrypoints } from \"../common/getInputHtmlEntrypoints.js\";\nimport { standardizeFileExtension } from \"../common/standardizeFileExtension.js\";\nimport { extractInjectedScripts } from \"./extractInjectedScripts.js\";\nimport { getBaseHref } from \"./getBaseHref.js\";\nimport { getFoundryToken } from \"./getFoundryToken.js\";\nimport { getWidgetIdOverrideMap } from \"./getWidgetIdOverrideMap.js\";\nimport { publishDevModeSettings } from \"./publishDevModeSettings.js\";\n\n// Location of the setup page assets\nconst DIR_DIST: string = typeof __dirname !== \"undefined\"\n ? __dirname\n : path.dirname(fileURLToPath(import.meta.url));\n\nexport function FoundryWidgetDevPlugin(): Plugin {\n // The root HTML entrypoints of the build process\n let htmlEntrypoints: string[];\n // Fully resolved paths to the entrypoint files, mapped to relative paths\n const codeEntrypoints: Record<string, string> = {};\n // Store the map of fully resolved config file paths to entrypoint file paths\n const configFileToEntrypoint: Record<string, string> = {};\n\n return {\n name: DEV_PLUGIN_ID,\n enforce: \"pre\",\n // Only apply this plugin during development, skip during tests and build-mode module evaluation\n apply(config, { command }) {\n if (\n config.mode === MODULE_EVALUATION_MODE || process.env.VITEST != null\n ) {\n return false;\n }\n return command === \"serve\";\n },\n\n /**\n * Capture the entrypoints from the Vite config so that we can manually load them on our\n * setup page and trigger module parsing.\n */\n buildStart(options) {\n htmlEntrypoints = getInputHtmlEntrypoints(options);\n },\n\n /**\n * Check for the required token environment variable in dev mode.\n */\n config(resolvedConfig) {\n getFoundryToken(resolvedConfig.mode);\n },\n\n /**\n * Configure the Vite server to serve the setup page and handle the finish endpoint. This\n * endpoint will set the widget overrides in Foundry and enable dev mode.\n */\n configureServer(server) {\n // Override the printUrls function to print the setup page URL\n server.printUrls = () => printSetupPageUrl(server);\n\n /**\n * Redirect `./.palantir/setup` to `./.palantir/setup/` to ensure that relative paths work\n * correctly. Relative paths must be used so that the dev server UI can be accessed on\n * non-root paths.\n */\n server.middlewares.use(\n serverPath(server, SETUP_PATH),\n (req, res, next) => {\n if (req.originalUrl?.endsWith(serverPath(server, SETUP_PATH))) {\n res.statusCode = 301;\n res.setHeader(\"Location\", `${serverPath(server, SETUP_PATH)}/`);\n res.end();\n } else {\n next();\n }\n },\n );\n\n /**\n * Serve the setup page that will load the entrypoints in iframes and trigger the finish\n * endpoint once widgets have been loaded.\n */\n server.middlewares.use(\n serverPath(server, SETUP_PATH),\n sirv(path.resolve(DIR_DIST, \"../../site\"), {\n single: true,\n dev: true,\n }),\n );\n\n /**\n * Make the entrypoints available to the setup page so that it can load them in iframes in\n * order to trigger module parsing.\n */\n server.middlewares.use(\n serverPath(server, ENTRYPOINTS_PATH),\n (_, res) => {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(\n JSON.stringify(\n htmlEntrypoints.map((entrypoint) =>\n serverPath(server, entrypoint)\n ),\n ),\n );\n },\n );\n\n /**\n * Finish the setup process by setting the widget overrides in Foundry and enabling dev mode.\n */\n server.middlewares.use(\n serverPath(server, FINISH_PATH),\n async (req, res) => {\n // Check for too many attempts\n if (req.url == null) {\n throw new Error(\"Request URL is undefined\");\n }\n const url = new URL(req.url, `http://${req.headers.host}`);\n const numAttempts = parseInt(url.searchParams.get(\"attempt\") ?? \"0\");\n if (numAttempts >= 10) {\n const errorMessage =\n \"Timed out waiting for widget config files to be parsed. Are you sure a widget config is imported?\";\n server.config.logger.error(errorMessage);\n res.setHeader(\"Content-Type\", \"application/json\");\n res.statusCode = 500;\n res.end(\n JSON.stringify({\n status: \"error\",\n error: errorMessage,\n }),\n );\n return;\n }\n\n // Wait for the setup page to trigger the parsing of the config files\n const numEntrypoints = htmlEntrypoints.length;\n const numConfigFiles = Object.keys(configFileToEntrypoint).length;\n if (numConfigFiles < numEntrypoints) {\n server.config.logger.info(\n `Waiting for widget config files to be parsed, found ${numConfigFiles} config files out of`\n + ` ${numEntrypoints} HTML entrypoints.`,\n );\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(JSON.stringify({ status: \"pending\" }));\n return;\n }\n\n // Prepare the widget overrides and finish the setup process\n const widgetIdToOverrides = await getWidgetIdOverrideMap(\n server,\n codeEntrypoints,\n configFileToEntrypoint,\n getBaseHref(server),\n );\n await publishDevModeSettings(\n server,\n widgetIdToOverrides,\n getBaseHref(server),\n res,\n );\n },\n );\n\n /**\n * Serve scripts that would usually be injected into the HTML if Vite had control over the\n * serving of index HTML pages. This is necessary to ensure that plugins like React refresh\n * work correctly.\n */\n server.middlewares.use(\n serverPath(server, VITE_INJECTIONS_PATH),\n async (_, res) => {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Content-Type\", \"application/javascript\");\n const injectedScripts = await extractInjectedScripts(server);\n res.end(injectedScripts.inlineScripts.join(\"\\n\"));\n },\n );\n },\n\n /**\n * As module imports are resolved, we need to capture the entrypoint file paths and the config\n * file paths that are imported from them.\n */\n resolveId(source, importer) {\n if (importer == null) {\n return;\n }\n\n // Standardize the source file extension and get the full path\n const standardizedSource = standardizeFileExtension(\n getFullSourcePath(\n // If the source path is absolute, resolve it against the current working directory\n source.startsWith(\"/\") ? path.join(process.cwd(), source) : source,\n importer,\n ),\n );\n // Importers are already full paths, so just standardize the extension\n // Normalize to ensure consistent path separators on Windows\n const standardizedImporter = standardizeFileExtension(\n path.normalize(importer),\n );\n\n // In dev mode all entrypoints have a generic HTML importer value\n if (\n importer.endsWith(\"index.html\") && !standardizedSource.includes(\"@fs\")\n ) {\n // Store the fully resolved path and the relative path, as we need the former for mapping\n // config files to entrypoints and the latter as a dev mode override script\n codeEntrypoints[standardizedSource] = source;\n }\n\n // Look for config files that are imported from an entrypoint file\n // Also check the config file being imported is in src to avoid picking up imports for other\n // project files like foundry.config.json / eslint.config.mjs when tailwind is used.\n if (\n standardizedSource.replace(/\\.[^/.]+$/, \"\").endsWith(CONFIG_FILE_SUFFIX)\n && standardizedSource.includes(\"/src/\")\n && codeEntrypoints[standardizedImporter] != null\n ) {\n const fullSourcePath = standardizeFileExtension(\n getFullSourcePath(source, standardizedImporter),\n );\n configFileToEntrypoint[fullSourcePath] = standardizedImporter;\n }\n },\n };\n}\n\n/**\n * During the resolution phase source are given as relative paths to the importer\n */\nfunction getFullSourcePath(source: string, importer: string): string {\n return path.resolve(path.dirname(importer), source);\n}\n\nfunction serverPath(server: ViteDevServer, subPath: string): string {\n // Don't use Windows-style paths when constructing URL paths for the HTTP server\n return path.posix.resolve(server.config.base, subPath);\n}\n\nfunction printSetupPageUrl(server: ViteDevServer) {\n const setupRoute = `${getBaseHref(server)}${SETUP_PATH}/`;\n server.config.logger.info(\n ` ${color.green(\"➜\")} ${\n color.bold(\"Click to enter developer mode for your widget set\")\n }: ${color.green(setupRoute)}`,\n );\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -18,6 +18,21 @@ import { loadFoundryConfig } from "@osdk/foundry-config-json";
|
|
|
18
18
|
import { inspect } from "node:util";
|
|
19
19
|
import { getCodeWorkspacesFoundryUrl, isCodeWorkspacesMode } from "./codeWorkspacesMode.js";
|
|
20
20
|
import { enableDevMode, setWidgetSetSettings } from "./network.js";
|
|
21
|
+
class ResponseError extends Error {
|
|
22
|
+
// To avoid inspect() from logging the error response since it's already logged
|
|
23
|
+
#response;
|
|
24
|
+
constructor(message, response) {
|
|
25
|
+
super(message);
|
|
26
|
+
try {
|
|
27
|
+
this.#response = JSON.stringify(JSON.parse(response), null, 4);
|
|
28
|
+
} catch {
|
|
29
|
+
this.#response = response;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
get response() {
|
|
33
|
+
return this.#response;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
21
36
|
|
|
22
37
|
/**
|
|
23
38
|
* Finish the setup process by setting the widget overrides in Foundry and enabling dev mode.
|
|
@@ -35,14 +50,14 @@ export async function publishDevModeSettings(server, widgetIdToOverrides, baseHr
|
|
|
35
50
|
server.config.logger.warn(`Unable to set widget settings in Foundry: ${settingsResponse.statusText}`);
|
|
36
51
|
const responseContent = await settingsResponse.text();
|
|
37
52
|
server.config.logger.warn(responseContent);
|
|
38
|
-
throw new
|
|
53
|
+
throw new ResponseError(`Unable to set widget settings in Foundry: ${settingsResponse.statusText}`, responseContent);
|
|
39
54
|
}
|
|
40
55
|
const enableResponse = await enableDevMode(foundryUrl, server.config.mode);
|
|
41
56
|
if (enableResponse.status !== 200) {
|
|
42
57
|
server.config.logger.warn(`Unable to enable dev mode in Foundry: ${enableResponse.statusText}`);
|
|
43
58
|
const responseContent = await enableResponse.text();
|
|
44
59
|
server.config.logger.warn(responseContent);
|
|
45
|
-
throw new
|
|
60
|
+
throw new ResponseError(`Unable to enable dev mode in Foundry: ${enableResponse.statusText}`, responseContent);
|
|
46
61
|
}
|
|
47
62
|
res.setHeader("Content-Type", "application/json");
|
|
48
63
|
res.end(JSON.stringify({
|
|
@@ -56,7 +71,8 @@ export async function publishDevModeSettings(server, widgetIdToOverrides, baseHr
|
|
|
56
71
|
res.statusCode = 500;
|
|
57
72
|
res.end(JSON.stringify({
|
|
58
73
|
status: "error",
|
|
59
|
-
error: inspect(error)
|
|
74
|
+
error: inspect(error),
|
|
75
|
+
response: error instanceof ResponseError ? error.response : undefined
|
|
60
76
|
}));
|
|
61
77
|
}
|
|
62
78
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publishDevModeSettings.js","names":["loadFoundryConfig","inspect","getCodeWorkspacesFoundryUrl","isCodeWorkspacesMode","enableDevMode","setWidgetSetSettings","publishDevModeSettings","server","widgetIdToOverrides","baseHref","res","foundryConfig","
|
|
1
|
+
{"version":3,"file":"publishDevModeSettings.js","names":["loadFoundryConfig","inspect","getCodeWorkspacesFoundryUrl","isCodeWorkspacesMode","enableDevMode","setWidgetSetSettings","ResponseError","Error","response","constructor","message","JSON","stringify","parse","publishDevModeSettings","server","widgetIdToOverrides","baseHref","res","foundryConfig","foundryUrl","config","mode","widgetSetRid","widgetSet","rid","settingsResponse","status","logger","warn","statusText","responseContent","text","enableResponse","setHeader","end","redirectUrl","error","statusCode","undefined"],"sources":["publishDevModeSettings.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { loadFoundryConfig } from \"@osdk/foundry-config-json\";\nimport type { ServerResponse } from \"node:http\";\nimport { inspect } from \"node:util\";\nimport type { ViteDevServer } from \"vite\";\nimport {\n getCodeWorkspacesFoundryUrl,\n isCodeWorkspacesMode,\n} from \"./codeWorkspacesMode.js\";\nimport { enableDevMode, setWidgetSetSettings } from \"./network.js\";\n\nclass ResponseError extends Error {\n // To avoid inspect() from logging the error response since it's already logged\n #response: string;\n\n constructor(message: string, response: string) {\n super(message);\n try {\n this.#response = JSON.stringify(JSON.parse(response), null, 4);\n } catch {\n this.#response = response;\n }\n }\n\n get response(): string {\n return this.#response;\n }\n}\n\n/**\n * Finish the setup process by setting the widget overrides in Foundry and enabling dev mode.\n */\nexport async function publishDevModeSettings(\n server: ViteDevServer,\n widgetIdToOverrides: Record<string, string[]>,\n baseHref: string,\n res: ServerResponse,\n): Promise<void> {\n try {\n const foundryConfig = await loadFoundryConfig(\"widgetSet\");\n if (foundryConfig == null) {\n throw new Error(\n \"foundry.config.json file not found.\",\n );\n }\n const foundryUrl = isCodeWorkspacesMode(server.config.mode)\n ? getCodeWorkspacesFoundryUrl()\n : foundryConfig.foundryConfig.foundryUrl;\n\n const widgetSetRid = foundryConfig.foundryConfig.widgetSet.rid;\n const settingsResponse = await setWidgetSetSettings(\n foundryUrl,\n widgetSetRid,\n widgetIdToOverrides,\n baseHref,\n server.config.mode,\n );\n if (settingsResponse.status !== 200) {\n server.config.logger.warn(\n `Unable to set widget settings in Foundry: ${settingsResponse.statusText}`,\n );\n const responseContent = await settingsResponse.text();\n server.config.logger.warn(responseContent);\n throw new ResponseError(\n `Unable to set widget settings in Foundry: ${settingsResponse.statusText}`,\n responseContent,\n );\n }\n\n const enableResponse = await enableDevMode(\n foundryUrl,\n server.config.mode,\n );\n if (enableResponse.status !== 200) {\n server.config.logger.warn(\n `Unable to enable dev mode in Foundry: ${enableResponse.statusText}`,\n );\n const responseContent = await enableResponse.text();\n server.config.logger.warn(responseContent);\n throw new ResponseError(\n `Unable to enable dev mode in Foundry: ${enableResponse.statusText}`,\n responseContent,\n );\n }\n\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(JSON.stringify({\n status: \"success\",\n // In Code Workspaces the preview UI automatically handles this redirect\n redirectUrl: isCodeWorkspacesMode(server.config.mode)\n ? null\n : `${foundryUrl}/workspace/custom-widgets/preview/${widgetSetRid}`,\n }));\n } catch (error: unknown) {\n server.config.logger.error(\n `Failed to start dev mode: ${(error as Error)}\\n\\n${inspect(error)}`,\n );\n res.setHeader(\"Content-Type\", \"application/json\");\n res.statusCode = 500;\n res.end(\n JSON.stringify(\n {\n status: \"error\",\n error: inspect(error),\n response: error instanceof ResponseError ? error.response : undefined,\n },\n ),\n );\n }\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,iBAAiB,QAAQ,2BAA2B;AAE7D,SAASC,OAAO,QAAQ,WAAW;AAEnC,SACEC,2BAA2B,EAC3BC,oBAAoB,QACf,yBAAyB;AAChC,SAASC,aAAa,EAAEC,oBAAoB,QAAQ,cAAc;AAElE,MAAMC,aAAa,SAASC,KAAK,CAAC;EAChC;EACA,CAACC,QAAQ;EAETC,WAAWA,CAACC,OAAe,EAAEF,QAAgB,EAAE;IAC7C,KAAK,CAACE,OAAO,CAAC;IACd,IAAI;MACF,IAAI,CAAC,CAACF,QAAQ,GAAGG,IAAI,CAACC,SAAS,CAACD,IAAI,CAACE,KAAK,CAACL,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC,CAAC,MAAM;MACN,IAAI,CAAC,CAACA,QAAQ,GAAGA,QAAQ;IAC3B;EACF;EAEA,IAAIA,QAAQA,CAAA,EAAW;IACrB,OAAO,IAAI,CAAC,CAACA,QAAQ;EACvB;AACF;;AAEA;AACA;AACA;AACA,OAAO,eAAeM,sBAAsBA,CAC1CC,MAAqB,EACrBC,mBAA6C,EAC7CC,QAAgB,EAChBC,GAAmB,EACJ;EACf,IAAI;IACF,MAAMC,aAAa,GAAG,MAAMnB,iBAAiB,CAAC,WAAW,CAAC;IAC1D,IAAImB,aAAa,IAAI,IAAI,EAAE;MACzB,MAAM,IAAIZ,KAAK,CACb,qCACF,CAAC;IACH;IACA,MAAMa,UAAU,GAAGjB,oBAAoB,CAACY,MAAM,CAACM,MAAM,CAACC,IAAI,CAAC,GACvDpB,2BAA2B,CAAC,CAAC,GAC7BiB,aAAa,CAACA,aAAa,CAACC,UAAU;IAE1C,MAAMG,YAAY,GAAGJ,aAAa,CAACA,aAAa,CAACK,SAAS,CAACC,GAAG;IAC9D,MAAMC,gBAAgB,GAAG,MAAMrB,oBAAoB,CACjDe,UAAU,EACVG,YAAY,EACZP,mBAAmB,EACnBC,QAAQ,EACRF,MAAM,CAACM,MAAM,CAACC,IAChB,CAAC;IACD,IAAII,gBAAgB,CAACC,MAAM,KAAK,GAAG,EAAE;MACnCZ,MAAM,CAACM,MAAM,CAACO,MAAM,CAACC,IAAI,CACvB,6CAA6CH,gBAAgB,CAACI,UAAU,EAC1E,CAAC;MACD,MAAMC,eAAe,GAAG,MAAML,gBAAgB,CAACM,IAAI,CAAC,CAAC;MACrDjB,MAAM,CAACM,MAAM,CAACO,MAAM,CAACC,IAAI,CAACE,eAAe,CAAC;MAC1C,MAAM,IAAIzB,aAAa,CACrB,6CAA6CoB,gBAAgB,CAACI,UAAU,EAAE,EAC1EC,eACF,CAAC;IACH;IAEA,MAAME,cAAc,GAAG,MAAM7B,aAAa,CACxCgB,UAAU,EACVL,MAAM,CAACM,MAAM,CAACC,IAChB,CAAC;IACD,IAAIW,cAAc,CAACN,MAAM,KAAK,GAAG,EAAE;MACjCZ,MAAM,CAACM,MAAM,CAACO,MAAM,CAACC,IAAI,CACvB,yCAAyCI,cAAc,CAACH,UAAU,EACpE,CAAC;MACD,MAAMC,eAAe,GAAG,MAAME,cAAc,CAACD,IAAI,CAAC,CAAC;MACnDjB,MAAM,CAACM,MAAM,CAACO,MAAM,CAACC,IAAI,CAACE,eAAe,CAAC;MAC1C,MAAM,IAAIzB,aAAa,CACrB,yCAAyC2B,cAAc,CAACH,UAAU,EAAE,EACpEC,eACF,CAAC;IACH;IAEAb,GAAG,CAACgB,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC;IACjDhB,GAAG,CAACiB,GAAG,CAACxB,IAAI,CAACC,SAAS,CAAC;MACrBe,MAAM,EAAE,SAAS;MACjB;MACAS,WAAW,EAAEjC,oBAAoB,CAACY,MAAM,CAACM,MAAM,CAACC,IAAI,CAAC,GACjD,IAAI,GACJ,GAAGF,UAAU,qCAAqCG,YAAY;IACpE,CAAC,CAAC,CAAC;EACL,CAAC,CAAC,OAAOc,KAAc,EAAE;IACvBtB,MAAM,CAACM,MAAM,CAACO,MAAM,CAACS,KAAK,CACxB,6BAA8BA,KAAK,OAAiBpC,OAAO,CAACoC,KAAK,CAAC,EACpE,CAAC;IACDnB,GAAG,CAACgB,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC;IACjDhB,GAAG,CAACoB,UAAU,GAAG,GAAG;IACpBpB,GAAG,CAACiB,GAAG,CACLxB,IAAI,CAACC,SAAS,CACZ;MACEe,MAAM,EAAE,OAAO;MACfU,KAAK,EAAEpC,OAAO,CAACoC,KAAK,CAAC;MACrB7B,QAAQ,EAAE6B,KAAK,YAAY/B,aAAa,GAAG+B,KAAK,CAAC7B,QAAQ,GAAG+B;IAC9D,CACF,CACF,CAAC;EACH;AACF","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{I as n}from"./index-DLOviMB1.js";import{I as e}from"./index-B-fsa5Ru.js";import{p as r,I as s}from"./index-
|
|
1
|
+
import{I as n}from"./index-DLOviMB1.js";import{I as e}from"./index-B-fsa5Ru.js";import{p as r,I as s}from"./index-C5U_5Xge.js";function I(o,t){var a=r(o);return t===s.STANDARD?n[a]:e[a]}function p(o){return r(o)}export{n as IconSvgPaths16,e as IconSvgPaths20,I as getIconPaths,p as iconNameToPathsRecordKey};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./allPaths-
|
|
2
|
-
import{_ as o,a as i,b as n}from"./index-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./allPaths-DAStXfot.js","./index-DLOviMB1.js","./index-B-fsa5Ru.js","./index-C5U_5Xge.js","./index-DjCw4U9Z.css"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{_ as o,a as i,b as n}from"./index-C5U_5Xge.js";var _=function(e,a){return o(void 0,void 0,void 0,function(){var t;return i(this,function(r){switch(r.label){case 0:return[4,n(()=>import("./allPaths-DAStXfot.js"),__vite__mapDeps([0,1,2,3,4]),import.meta.url)];case 1:return t=r.sent().getIconPaths,[2,t(e,a)]}})})};export{_ as allPathsLoader};
|