@upstart.gg/vite-plugins 0.1.1 → 0.1.3

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,5 +1,5 @@
1
1
  import { UpstartBrandingPluginOptions } from "./types.js";
2
- import * as unplugin8 from "unplugin";
2
+ import * as unplugin5 from "unplugin";
3
3
 
4
4
  //#region src/vite-plugin-upstart-branding/plugin.d.ts
5
5
 
@@ -10,8 +10,8 @@ import * as unplugin8 from "unplugin";
10
10
  * The badge appears at the bottom of the page, then fades out after a
11
11
  * few seconds or when the user scrolls.
12
12
  */
13
- declare const upstartBranding: unplugin8.UnpluginInstance<UpstartBrandingPluginOptions, boolean>;
14
- declare const _default: (options: UpstartBrandingPluginOptions) => unplugin8.VitePlugin<any> | unplugin8.VitePlugin<any>[];
13
+ declare const upstartBranding: unplugin5.UnpluginInstance<UpstartBrandingPluginOptions, boolean>;
14
+ declare const _default: (options: UpstartBrandingPluginOptions) => unplugin5.VitePlugin<any> | unplugin5.VitePlugin<any>[];
15
15
  //#endregion
16
16
  export { _default as default, upstartBranding };
17
17
  //# sourceMappingURL=plugin.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { UpstartEditorPluginOptions } from "./runtime/types.js";
2
- import * as unplugin5 from "unplugin";
2
+ import * as unplugin8 from "unplugin";
3
3
 
4
4
  //#region src/vite-plugin-upstart-editor/plugin.d.ts
5
5
 
@@ -8,8 +8,8 @@ import * as unplugin5 from "unplugin";
8
8
  *
9
9
  * Injects the editor runtime into the app entry during build.
10
10
  */
11
- declare const upstartEditor: unplugin5.UnpluginInstance<UpstartEditorPluginOptions, boolean>;
12
- declare const _default: (options: UpstartEditorPluginOptions) => unplugin5.VitePlugin<any> | unplugin5.VitePlugin<any>[];
11
+ declare const upstartEditor: unplugin8.UnpluginInstance<UpstartEditorPluginOptions, boolean>;
12
+ declare const _default: (options: UpstartEditorPluginOptions) => unplugin8.VitePlugin<any> | unplugin8.VitePlugin<any>[];
13
13
  //#endregion
14
14
  export { _default as default, upstartEditor };
15
15
  //# sourceMappingURL=plugin.d.ts.map
@@ -38,6 +38,19 @@ function initUpstartEditor() {
38
38
  console.log("[Upstart Editor] Initializing...");
39
39
  isInitialized = true;
40
40
  window.addEventListener("message", handleParentMessage);
41
+ window.addEventListener("popstate", () => {
42
+ sendToParent({ type: "editor-navigated" });
43
+ });
44
+ const originalPushState = history.pushState.bind(history);
45
+ history.pushState = (...args) => {
46
+ originalPushState(...args);
47
+ sendToParent({ type: "editor-navigated" });
48
+ };
49
+ const originalReplaceState = history.replaceState.bind(history);
50
+ history.replaceState = (...args) => {
51
+ originalReplaceState(...args);
52
+ sendToParent({ type: "editor-navigated" });
53
+ };
41
54
  initTextEditor();
42
55
  initClickHandler();
43
56
  initHoverOverlay();
@@ -71,15 +84,7 @@ function handleParentMessage(event) {
71
84
  }
72
85
  if (message.type === "set-mode") {
73
86
  console.log("Setting editor mode to:", message.mode);
74
- if ("scheduler" in globalThis) globalThis.scheduler.postTask(() => {
75
- setMode(message.mode);
76
- }, {
77
- delay: 250,
78
- priority: "background"
79
- });
80
- else setTimeout(() => {
81
- setMode(message.mode);
82
- }, 250);
87
+ setMode(message.mode);
83
88
  }
84
89
  }
85
90
  function enableEditMode() {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["currentMode: EditorMode"],"sources":["../../../src/vite-plugin-upstart-editor/runtime/index.ts"],"sourcesContent":["import { initClickHandler } from \"./click-handler.js\";\nimport { initHoverOverlay, hideOverlays } from \"./hover-overlay.js\";\nimport { initErrorHandler } from \"./error-handler.js\";\nimport { initTextEditor, activateAllEditors, destroyAllActiveEditors } from \"./text-editor.js\";\nimport { sendToParent } from \"./utils.js\";\nimport type { EditorMode, UpstartParentMessage } from \"./types.js\";\n\nlet currentMode: EditorMode = \"preview\";\nlet isInitialized = false;\n\n/**\n * Get the current editor mode.\n */\nexport function getCurrentMode(): EditorMode {\n return currentMode;\n}\n\n/**\n * Set the current editor mode.\n */\nexport function setMode(mode: EditorMode): void {\n currentMode = mode;\n console.log(`[Upstart Editor] Setting mode to: ${mode}`);\n if (mode === \"edit\") {\n enableEditMode();\n } else {\n disableEditMode();\n }\n}\n\n/**\n * Initialize the Upstart editor runtime.\n */\nexport function initUpstartEditor(): void {\n if (typeof window === \"undefined\") {\n console.warn(\"[Upstart Editor] Cannot initialize editor: not running in a browser environment\");\n return;\n }\n\n if (isInitialized) {\n console.log(\"[Upstart Editor] Editor is already initialized\");\n return;\n }\n\n try {\n console.log(\"[Upstart Editor] Initializing...\");\n\n isInitialized = true;\n\n window.addEventListener(\"message\", handleParentMessage);\n\n initTextEditor();\n initClickHandler();\n initHoverOverlay();\n initErrorHandler();\n\n sendToParent({ type: \"editor-ready\" });\n } catch (error) {\n console.error(\"[Upstart Editor] Initialization failed:\", error);\n sendToParent({\n type: \"editor-error\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n}\n\nconst ALLOWED_ORIGINS = [\"http://localhost:8080\", /upstart.gg$/];\n\nconst matchAllowedOrigins = (origin: string) => {\n return ALLOWED_ORIGINS.some((allowedOrigin) => {\n if (typeof allowedOrigin === \"string\") {\n return origin === allowedOrigin;\n } else if (allowedOrigin instanceof RegExp) {\n return allowedOrigin.test(origin);\n }\n return false;\n });\n};\n\nfunction handleParentMessage(event: MessageEvent): void {\n const message = event.data as UpstartParentMessage | undefined;\n\n console.log(\"[Upstart Editor] Received message from parent:\", { event, message });\n\n if (!message || !matchAllowedOrigins(event.origin)) {\n console.warn(\"[Upstart Editor] Ignoring message from unknown source:\", event.origin);\n return;\n }\n\n if (message.type === \"set-mode\") {\n console.log(\"Setting editor mode to:\", message.mode);\n if (\"scheduler\" in globalThis) {\n // @ts-expect-error not yet in TS types\n globalThis.scheduler.postTask(\n () => {\n setMode(message.mode);\n },\n { delay: 250, priority: \"background\" },\n );\n } else {\n setTimeout(() => {\n setMode(message.mode);\n }, 250);\n }\n }\n}\n\nfunction enableEditMode(): void {\n console.log(\"[Upstart Editor] Edit mode enabled\");\n activateAllEditors();\n}\n\nfunction disableEditMode(): void {\n console.log(\"[Upstart Editor] Preview mode enabled\");\n destroyAllActiveEditors();\n hideOverlays();\n}\n\nexport { initTextEditor } from \"./text-editor.js\";\nexport { initClickHandler } from \"./click-handler.js\";\nexport { initErrorHandler } from \"./error-handler.js\";\nexport { initHoverOverlay } from \"./hover-overlay.js\";\nexport { sendToParent } from \"./utils.js\";\nexport type { EditorMessage, UpstartEditorMessage } from \"./types.js\";\n"],"mappings":";;;;;;;AAOA,IAAIA,cAA0B;AAC9B,IAAI,gBAAgB;;;;AAKpB,SAAgB,iBAA6B;AAC3C,QAAO;;;;;AAMT,SAAgB,QAAQ,MAAwB;AAC9C,eAAc;AACd,SAAQ,IAAI,qCAAqC,OAAO;AACxD,KAAI,SAAS,OACX,iBAAgB;KAEhB,kBAAiB;;;;;AAOrB,SAAgB,oBAA0B;AACxC,KAAI,OAAO,WAAW,aAAa;AACjC,UAAQ,KAAK,kFAAkF;AAC/F;;AAGF,KAAI,eAAe;AACjB,UAAQ,IAAI,iDAAiD;AAC7D;;AAGF,KAAI;AACF,UAAQ,IAAI,mCAAmC;AAE/C,kBAAgB;AAEhB,SAAO,iBAAiB,WAAW,oBAAoB;AAEvD,kBAAgB;AAChB,oBAAkB;AAClB,oBAAkB;AAClB,oBAAkB;AAElB,eAAa,EAAE,MAAM,gBAAgB,CAAC;UAC/B,OAAO;AACd,UAAQ,MAAM,2CAA2C,MAAM;AAC/D,eAAa;GACX,MAAM;GACN,OAAO,iBAAiB,QAAQ,MAAM,UAAU;GACjD,CAAC;;;AAIN,MAAM,kBAAkB,CAAC,yBAAyB,cAAc;AAEhE,MAAM,uBAAuB,WAAmB;AAC9C,QAAO,gBAAgB,MAAM,kBAAkB;AAC7C,MAAI,OAAO,kBAAkB,SAC3B,QAAO,WAAW;WACT,yBAAyB,OAClC,QAAO,cAAc,KAAK,OAAO;AAEnC,SAAO;GACP;;AAGJ,SAAS,oBAAoB,OAA2B;CACtD,MAAM,UAAU,MAAM;AAEtB,SAAQ,IAAI,kDAAkD;EAAE;EAAO;EAAS,CAAC;AAEjF,KAAI,CAAC,WAAW,CAAC,oBAAoB,MAAM,OAAO,EAAE;AAClD,UAAQ,KAAK,0DAA0D,MAAM,OAAO;AACpF;;AAGF,KAAI,QAAQ,SAAS,YAAY;AAC/B,UAAQ,IAAI,2BAA2B,QAAQ,KAAK;AACpD,MAAI,eAAe,WAEjB,YAAW,UAAU,eACb;AACJ,WAAQ,QAAQ,KAAK;KAEvB;GAAE,OAAO;GAAK,UAAU;GAAc,CACvC;MAED,kBAAiB;AACf,WAAQ,QAAQ,KAAK;KACpB,IAAI;;;AAKb,SAAS,iBAAuB;AAC9B,SAAQ,IAAI,qCAAqC;AACjD,qBAAoB;;AAGtB,SAAS,kBAAwB;AAC/B,SAAQ,IAAI,wCAAwC;AACpD,0BAAyB;AACzB,eAAc"}
1
+ {"version":3,"file":"index.js","names":["currentMode: EditorMode"],"sources":["../../../src/vite-plugin-upstart-editor/runtime/index.ts"],"sourcesContent":["import { initClickHandler } from \"./click-handler.js\";\nimport { initHoverOverlay, hideOverlays } from \"./hover-overlay.js\";\nimport { initErrorHandler } from \"./error-handler.js\";\nimport { initTextEditor, activateAllEditors, destroyAllActiveEditors } from \"./text-editor.js\";\nimport { sendToParent } from \"./utils.js\";\nimport type { EditorMode, UpstartParentMessage } from \"./types.js\";\n\nlet currentMode: EditorMode = \"preview\";\nlet isInitialized = false;\n\n/**\n * Get the current editor mode.\n */\nexport function getCurrentMode(): EditorMode {\n return currentMode;\n}\n\n/**\n * Set the current editor mode.\n */\nexport function setMode(mode: EditorMode): void {\n currentMode = mode;\n console.log(`[Upstart Editor] Setting mode to: ${mode}`);\n if (mode === \"edit\") {\n enableEditMode();\n } else {\n disableEditMode();\n }\n}\n\n/**\n * Initialize the Upstart editor runtime.\n */\nexport function initUpstartEditor(): void {\n if (typeof window === \"undefined\") {\n console.warn(\"[Upstart Editor] Cannot initialize editor: not running in a browser environment\");\n return;\n }\n\n if (isInitialized) {\n console.log(\"[Upstart Editor] Editor is already initialized\");\n return;\n }\n\n try {\n console.log(\"[Upstart Editor] Initializing...\");\n\n isInitialized = true;\n\n window.addEventListener(\"message\", handleParentMessage);\n\n // Notify parent on SPA navigation so it can resend the current editMode\n window.addEventListener(\"popstate\", () => {\n sendToParent({ type: \"editor-navigated\" });\n });\n const originalPushState = history.pushState.bind(history);\n history.pushState = (...args) => {\n originalPushState(...args);\n sendToParent({ type: \"editor-navigated\" });\n };\n const originalReplaceState = history.replaceState.bind(history);\n history.replaceState = (...args) => {\n originalReplaceState(...args);\n sendToParent({ type: \"editor-navigated\" });\n };\n\n initTextEditor();\n initClickHandler();\n initHoverOverlay();\n initErrorHandler();\n\n sendToParent({ type: \"editor-ready\" });\n } catch (error) {\n console.error(\"[Upstart Editor] Initialization failed:\", error);\n sendToParent({\n type: \"editor-error\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n}\n\nconst ALLOWED_ORIGINS = [\"http://localhost:8080\", /upstart.gg$/];\n\nconst matchAllowedOrigins = (origin: string) => {\n return ALLOWED_ORIGINS.some((allowedOrigin) => {\n if (typeof allowedOrigin === \"string\") {\n return origin === allowedOrigin;\n } else if (allowedOrigin instanceof RegExp) {\n return allowedOrigin.test(origin);\n }\n return false;\n });\n};\n\nfunction handleParentMessage(event: MessageEvent): void {\n const message = event.data as UpstartParentMessage | undefined;\n\n console.log(\"[Upstart Editor] Received message from parent:\", { event, message });\n\n if (!message || !matchAllowedOrigins(event.origin)) {\n console.warn(\"[Upstart Editor] Ignoring message from unknown source:\", event.origin);\n return;\n }\n\n if (message.type === \"set-mode\") {\n console.log(\"Setting editor mode to:\", message.mode);\n setMode(message.mode);\n }\n}\n\nfunction enableEditMode(): void {\n console.log(\"[Upstart Editor] Edit mode enabled\");\n activateAllEditors();\n}\n\nfunction disableEditMode(): void {\n console.log(\"[Upstart Editor] Preview mode enabled\");\n destroyAllActiveEditors();\n hideOverlays();\n}\n\nexport { initTextEditor } from \"./text-editor.js\";\nexport { initClickHandler } from \"./click-handler.js\";\nexport { initErrorHandler } from \"./error-handler.js\";\nexport { initHoverOverlay } from \"./hover-overlay.js\";\nexport { sendToParent } from \"./utils.js\";\nexport type { EditorMessage, UpstartEditorMessage } from \"./types.js\";\n"],"mappings":";;;;;;;AAOA,IAAIA,cAA0B;AAC9B,IAAI,gBAAgB;;;;AAKpB,SAAgB,iBAA6B;AAC3C,QAAO;;;;;AAMT,SAAgB,QAAQ,MAAwB;AAC9C,eAAc;AACd,SAAQ,IAAI,qCAAqC,OAAO;AACxD,KAAI,SAAS,OACX,iBAAgB;KAEhB,kBAAiB;;;;;AAOrB,SAAgB,oBAA0B;AACxC,KAAI,OAAO,WAAW,aAAa;AACjC,UAAQ,KAAK,kFAAkF;AAC/F;;AAGF,KAAI,eAAe;AACjB,UAAQ,IAAI,iDAAiD;AAC7D;;AAGF,KAAI;AACF,UAAQ,IAAI,mCAAmC;AAE/C,kBAAgB;AAEhB,SAAO,iBAAiB,WAAW,oBAAoB;AAGvD,SAAO,iBAAiB,kBAAkB;AACxC,gBAAa,EAAE,MAAM,oBAAoB,CAAC;IAC1C;EACF,MAAM,oBAAoB,QAAQ,UAAU,KAAK,QAAQ;AACzD,UAAQ,aAAa,GAAG,SAAS;AAC/B,qBAAkB,GAAG,KAAK;AAC1B,gBAAa,EAAE,MAAM,oBAAoB,CAAC;;EAE5C,MAAM,uBAAuB,QAAQ,aAAa,KAAK,QAAQ;AAC/D,UAAQ,gBAAgB,GAAG,SAAS;AAClC,wBAAqB,GAAG,KAAK;AAC7B,gBAAa,EAAE,MAAM,oBAAoB,CAAC;;AAG5C,kBAAgB;AAChB,oBAAkB;AAClB,oBAAkB;AAClB,oBAAkB;AAElB,eAAa,EAAE,MAAM,gBAAgB,CAAC;UAC/B,OAAO;AACd,UAAQ,MAAM,2CAA2C,MAAM;AAC/D,eAAa;GACX,MAAM;GACN,OAAO,iBAAiB,QAAQ,MAAM,UAAU;GACjD,CAAC;;;AAIN,MAAM,kBAAkB,CAAC,yBAAyB,cAAc;AAEhE,MAAM,uBAAuB,WAAmB;AAC9C,QAAO,gBAAgB,MAAM,kBAAkB;AAC7C,MAAI,OAAO,kBAAkB,SAC3B,QAAO,WAAW;WACT,yBAAyB,OAClC,QAAO,cAAc,KAAK,OAAO;AAEnC,SAAO;GACP;;AAGJ,SAAS,oBAAoB,OAA2B;CACtD,MAAM,UAAU,MAAM;AAEtB,SAAQ,IAAI,kDAAkD;EAAE;EAAO;EAAS,CAAC;AAEjF,KAAI,CAAC,WAAW,CAAC,oBAAoB,MAAM,OAAO,EAAE;AAClD,UAAQ,KAAK,0DAA0D,MAAM,OAAO;AACpF;;AAGF,KAAI,QAAQ,SAAS,YAAY;AAC/B,UAAQ,IAAI,2BAA2B,QAAQ,KAAK;AACpD,UAAQ,QAAQ,KAAK;;;AAIzB,SAAS,iBAAuB;AAC9B,SAAQ,IAAI,qCAAqC;AACjD,qBAAoB;;AAGtB,SAAS,kBAAwB;AAC/B,SAAQ,IAAI,wCAAwC;AACpD,0BAAyB;AACzB,eAAc"}
@@ -39,6 +39,8 @@ type EditorMessage = {
39
39
  payload: PayloadEditText;
40
40
  } | {
41
41
  type: "editor-ready";
42
+ } | {
43
+ type: "editor-navigated";
42
44
  } | {
43
45
  type: "editor-error";
44
46
  error: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/types.ts"],"sourcesContent":[],"mappings":";;;;;;;AAMA;AAKY,KALA,UAAA,GAKc,SAAA,GAAA,MAAA;AAK1B;AAYA;;AAEW,KAnBC,cAAA,GAmBD,YAAA,GAAA,aAAA,GAAA,OAAA;;;AAQX;AACkC,UAvBjB,aAAA,CAuBiB;EAGmB,GAAA,EAAA,MAAA;EASvC,IAAA,EAAA,MAAA;EAAa,KAAA,EAAA,MAAA;EAMf,MAAA,EAAA,MAAA;EAKK,KAAA,EAAA,MAAA;EASA,MAAA,EAAA,MAAA;AASjB;AA0CA;;;UA9FiB,cAAA;UACP;WACC;;QAEH;;;;;KAMI,aAAA;;WACsB;;;;;;;;;UAGmB;;;;;;;;;;;;UASvC;;;;;KAMF,aAAA;;QAA0C;;;;;UAKrC,oBAAA;;QAET;;;;;;UAOS,oBAAA;;QAET;;;;;;UAOS,oBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA0CA,0BAAA"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/types.ts"],"sourcesContent":[],"mappings":";;;;;;;AAMA;AAKY,KALA,UAAA,GAKc,SAAA,GAAA,MAAA;AAK1B;AAYA;;AAEW,KAnBC,cAAA,GAmBD,YAAA,GAAA,aAAA,GAAA,OAAA;;;AAQX;AACkC,UAvBjB,aAAA,CAuBiB;EAImB,GAAA,EAAA,MAAA;EASvC,IAAA,EAAA,MAAA;EAAa,KAAA,EAAA,MAAA;EAMf,MAAA,EAAA,MAAA;EAKK,KAAA,EAAA,MAAA;EASA,MAAA,EAAA,MAAA;AASjB;AA0CA;;;UA/FiB,cAAA;UACP;WACC;;QAEH;;;;;KAMI,aAAA;;WACsB;;;;;;;;;;;UAImB;;;;;;;;;;;;UASvC;;;;;KAMF,aAAA;;QAA0C;;;;;UAKrC,oBAAA;;QAET;;;;;;UAOS,oBAAA;;QAET;;;;;;UAOS,oBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA0CA,0BAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upstart.gg/vite-plugins",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -23,7 +23,7 @@
23
23
  "oxc-parser": "^0.101.0",
24
24
  "unplugin": "^2.3.11",
25
25
  "zimmerframe": "^1.1.4",
26
- "@upstart.gg/sdk": "^0.1.1"
26
+ "@upstart.gg/sdk": "^0.1.3"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/bun": "1.3.5",
@@ -111,7 +111,7 @@
111
111
  },
112
112
  "peerDependencies": {
113
113
  "zod": "4.2.1",
114
- "@upstart.gg/sdk": "^0.1.1"
114
+ "@upstart.gg/sdk": "^0.1.3"
115
115
  },
116
116
  "author": "Upstart",
117
117
  "publishConfig": {
@@ -49,6 +49,21 @@ export function initUpstartEditor(): void {
49
49
 
50
50
  window.addEventListener("message", handleParentMessage);
51
51
 
52
+ // Notify parent on SPA navigation so it can resend the current editMode
53
+ window.addEventListener("popstate", () => {
54
+ sendToParent({ type: "editor-navigated" });
55
+ });
56
+ const originalPushState = history.pushState.bind(history);
57
+ history.pushState = (...args) => {
58
+ originalPushState(...args);
59
+ sendToParent({ type: "editor-navigated" });
60
+ };
61
+ const originalReplaceState = history.replaceState.bind(history);
62
+ history.replaceState = (...args) => {
63
+ originalReplaceState(...args);
64
+ sendToParent({ type: "editor-navigated" });
65
+ };
66
+
52
67
  initTextEditor();
53
68
  initClickHandler();
54
69
  initHoverOverlay();
@@ -89,19 +104,7 @@ function handleParentMessage(event: MessageEvent): void {
89
104
 
90
105
  if (message.type === "set-mode") {
91
106
  console.log("Setting editor mode to:", message.mode);
92
- if ("scheduler" in globalThis) {
93
- // @ts-expect-error not yet in TS types
94
- globalThis.scheduler.postTask(
95
- () => {
96
- setMode(message.mode);
97
- },
98
- { delay: 250, priority: "background" },
99
- );
100
- } else {
101
- setTimeout(() => {
102
- setMode(message.mode);
103
- }, 250);
104
- }
107
+ setMode(message.mode);
105
108
  }
106
109
  }
107
110
 
@@ -39,6 +39,7 @@ export interface EditorInstance {
39
39
  export type EditorMessage =
40
40
  | { type: "text-edit"; payload: PayloadEditText }
41
41
  | { type: "editor-ready" }
42
+ | { type: "editor-navigated" }
42
43
  | { type: "editor-error"; error: string }
43
44
  | { type: "element-hovered"; hash: string; bounds: ElementBounds }
44
45
  | { type: "datasource-item-clicked"; datasourceId: string; recordId: string }