@harborclient/sdk 1.0.0 → 1.0.2

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.
Files changed (87) hide show
  1. package/dist/build/index.d.ts.map +1 -1
  2. package/dist/build/index.js +10 -1
  3. package/dist/client.d.ts +2 -2
  4. package/dist/components/Autocomplete/AutocompleteInput.d.ts +23 -0
  5. package/dist/components/Autocomplete/AutocompleteInput.d.ts.map +1 -0
  6. package/dist/components/Autocomplete/AutocompleteInput.js +26 -0
  7. package/dist/components/Autocomplete/SuggestionList.d.ts +45 -0
  8. package/dist/components/Autocomplete/SuggestionList.d.ts.map +1 -0
  9. package/dist/components/Autocomplete/SuggestionList.js +72 -0
  10. package/dist/components/Autocomplete/index.d.ts +6 -0
  11. package/dist/components/Autocomplete/index.d.ts.map +1 -0
  12. package/dist/components/Autocomplete/index.js +3 -0
  13. package/dist/components/Autocomplete/types.d.ts +19 -0
  14. package/dist/components/Autocomplete/types.d.ts.map +1 -0
  15. package/dist/components/Autocomplete/types.js +1 -0
  16. package/dist/components/Autocomplete/useAutocomplete.d.ts +84 -0
  17. package/dist/components/Autocomplete/useAutocomplete.d.ts.map +1 -0
  18. package/dist/components/Autocomplete/useAutocomplete.js +191 -0
  19. package/dist/components/Button/index.d.ts.map +1 -1
  20. package/dist/components/Button/index.js +8 -7
  21. package/dist/components/KeyValueEditor/index.d.ts +10 -1
  22. package/dist/components/KeyValueEditor/index.d.ts.map +1 -1
  23. package/dist/components/KeyValueEditor/index.js +4 -3
  24. package/dist/components/VariableInput/index.d.ts +6 -1
  25. package/dist/components/VariableInput/index.d.ts.map +1 -1
  26. package/dist/components/VariableInput/index.js +19 -3
  27. package/dist/components/index.d.ts +1 -0
  28. package/dist/components/index.d.ts.map +1 -1
  29. package/dist/components/index.js +1 -0
  30. package/dist/components/portalToBody.d.ts +2 -1
  31. package/dist/components/portalToBody.d.ts.map +1 -1
  32. package/dist/components/portalToBody.js +5 -4
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/main.d.ts +1 -1
  36. package/dist/main.d.ts.map +1 -1
  37. package/dist/runtime/hcBridge.js +0 -3
  38. package/dist/runtime/index.d.ts +1 -1
  39. package/dist/runtime/jsx-dev-runtime.d.ts +1 -1
  40. package/dist/runtime/store.d.ts +6 -1
  41. package/dist/runtime/store.d.ts.map +1 -1
  42. package/dist/runtime/store.js +8 -2
  43. package/dist/runtime/store.ts +11 -3
  44. package/package.json +3 -2
  45. package/dist/components/enhanceControl.test.d.ts +0 -2
  46. package/dist/components/enhanceControl.test.d.ts.map +0 -1
  47. package/dist/components/enhanceControl.test.js +0 -83
  48. package/dist/components/utils.test.d.ts +0 -2
  49. package/dist/components/utils.test.d.ts.map +0 -1
  50. package/dist/components/utils.test.js +0 -44
  51. package/dist/http/resolveRequest.test.d.ts +0 -2
  52. package/dist/http/resolveRequest.test.d.ts.map +0 -1
  53. package/dist/http/resolveRequest.test.js +0 -41
  54. package/dist/http/substitute.test.d.ts +0 -2
  55. package/dist/http/substitute.test.d.ts.map +0 -1
  56. package/dist/http/substitute.test.js +0 -93
  57. package/dist/pluginDatabaseApi.test.d.ts +0 -2
  58. package/dist/pluginDatabaseApi.test.d.ts.map +0 -1
  59. package/dist/pluginDatabaseApi.test.js +0 -65
  60. package/dist/runtime/index.test.d.ts +0 -2
  61. package/dist/runtime/index.test.d.ts.map +0 -1
  62. package/dist/runtime/index.test.js +0 -41
  63. package/dist/runtime/index.test.ts +0 -53
  64. package/dist/runtime-utils.test.d.ts +0 -2
  65. package/dist/runtime-utils.test.d.ts.map +0 -1
  66. package/dist/runtime-utils.test.js +0 -177
  67. package/dist/signing/signing.test.d.ts +0 -2
  68. package/dist/signing/signing.test.d.ts.map +0 -1
  69. package/dist/signing/signing.test.js +0 -100
  70. package/dist/storage/cappedList.test.d.ts +0 -2
  71. package/dist/storage/cappedList.test.d.ts.map +0 -1
  72. package/dist/storage/cappedList.test.js +0 -11
  73. package/dist/storage/validate.test.d.ts +0 -2
  74. package/dist/storage/validate.test.d.ts.map +0 -1
  75. package/dist/storage/validate.test.js +0 -78
  76. package/dist/store.test.d.ts +0 -2
  77. package/dist/store.test.d.ts.map +0 -1
  78. package/dist/store.test.js +0 -248
  79. package/dist/utilities.test.d.ts +0 -2
  80. package/dist/utilities.test.d.ts.map +0 -1
  81. package/dist/utilities.test.js +0 -16
  82. package/dist/variables/dynamic.test.d.ts +0 -2
  83. package/dist/variables/dynamic.test.d.ts.map +0 -1
  84. package/dist/variables/dynamic.test.js +0 -60
  85. package/dist/variables/tokens.test.d.ts +0 -2
  86. package/dist/variables/tokens.test.d.ts.map +0 -1
  87. package/dist/variables/tokens.test.js +0 -160
@@ -1,5 +1,6 @@
1
1
  import type { JSX, KeyboardEvent } from 'react';
2
2
  import type { Variable } from '../../types.js';
3
+ import type { AutocompleteSource } from '../Autocomplete/types.js';
3
4
  export interface Props {
4
5
  /**
5
6
  * Current input value.
@@ -47,6 +48,10 @@ export interface Props {
47
48
  * Id of the element that labels this input when using `aria-labelledby`.
48
49
  */
49
50
  'aria-labelledby'?: string;
51
+ /**
52
+ * Optional async source for value autocomplete suggestions.
53
+ */
54
+ source?: AutocompleteSource;
50
55
  }
51
56
  /**
52
57
  * Text input that highlights {{variable}} tokens and shows resolved values on hover.
@@ -54,5 +59,5 @@ export interface Props {
54
59
  * Token highlight color (`text-[#32D2E2]`) and the tooltip `app-no-drag` class rely on
55
60
  * host styling in HarborClient `styles.css`.
56
61
  */
57
- export declare function VariableInput({ value, onChange, variables, placeholder, onKeyDown, className, wrapperClassName, onEditVariable, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy }: Props): JSX.Element;
62
+ export declare function VariableInput({ value, onChange, variables, placeholder, onKeyDown, className, wrapperClassName, onEditVariable, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, source }: Props): JSX.Element;
58
63
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/VariableInput/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,aAAa,EAAc,MAAM,OAAO,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAkB/C,MAAM,WAAW,KAAK;IACpB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC;;OAEG;IACH,SAAS,EAAE,QAAQ,EAAE,CAAC;IAEtB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAEzD;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAE5B;;OAEG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,SAAS,EACT,WAAW,EACX,SAAS,EACT,SAAc,EACd,gBAAgB,EAChB,cAAc,EACd,EAAE,EACF,YAAY,EAAE,SAAS,EACvB,iBAAiB,EAAE,cAAc,EAClC,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CA0PrB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/VariableInput/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,aAAa,EAAc,MAAM,OAAO,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAS/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAYnE,MAAM,WAAW,KAAK;IACpB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC;;OAEG;IACH,SAAS,EAAE,QAAQ,EAAE,CAAC;IAEtB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAEzD;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAE5B;;OAEG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,SAAS,EACT,WAAW,EACX,SAAS,EACT,SAAc,EACd,gBAAgB,EAChB,cAAc,EACd,EAAE,EACF,YAAY,EAAE,SAAS,EACvB,iBAAiB,EAAE,cAAc,EACjC,MAAM,EACP,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CA0SrB"}
@@ -1,6 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "@harborclient/sdk/jsx-runtime";
2
2
  import { useEffect, useId, useMemo, useRef, useState } from '@harborclient/sdk/react';
3
3
  import { getDynamicVariableDescription, getVariableTokenAtOffset, getVariableTooltipContent, resolveVariable, tokenizeVariables } from '../../variables/index.js';
4
+ import { SuggestionList } from '../Autocomplete/SuggestionList.js';
5
+ import { useAutocomplete } from '../Autocomplete/useAutocomplete.js';
4
6
  import { Input } from '../forms/index.js';
5
7
  /**
6
8
  * Text input that highlights {{variable}} tokens and shows resolved values on hover.
@@ -8,13 +10,19 @@ import { Input } from '../forms/index.js';
8
10
  * Token highlight color (`text-[#32D2E2]`) and the tooltip `app-no-drag` class rely on
9
11
  * host styling in HarborClient `styles.css`.
10
12
  */
11
- export function VariableInput({ value, onChange, variables, placeholder, onKeyDown, className = '', wrapperClassName, onEditVariable, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy }) {
13
+ export function VariableInput({ value, onChange, variables, placeholder, onKeyDown, className = '', wrapperClassName, onEditVariable, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, source }) {
12
14
  const inputRef = useRef(null);
13
15
  const backdropRef = useRef(null);
14
16
  const spanRefs = useRef(new Map());
15
17
  const hideTimer = useRef(null);
16
18
  const [tooltip, setTooltip] = useState(null);
17
19
  const tooltipId = useId();
20
+ const { open: autocompleteOpen, items: autocompleteItems, activeIndex: autocompleteActiveIndex, listboxId, onFocus: openAutocomplete, onBlur: closeAutocomplete, onInputKeyDown, selectItem, setActiveIndex, closeSuggestions } = useAutocomplete({
21
+ source,
22
+ value,
23
+ onSelect: onChange,
24
+ anchorRef: inputRef
25
+ });
18
26
  /**
19
27
  * Splits the input value into plain text and {{variable}} token spans for highlighting.
20
28
  */
@@ -126,6 +134,9 @@ export function VariableInput({ value, onChange, variables, placeholder, onKeyDo
126
134
  * @param event - Keyboard event from the input.
127
135
  */
128
136
  const handleKeyDown = (event) => {
137
+ if (onInputKeyDown(event)) {
138
+ return;
139
+ }
129
140
  if (event.key === 'Escape' && tooltip) {
130
141
  event.preventDefault();
131
142
  setTooltip(null);
@@ -154,10 +165,15 @@ export function VariableInput({ value, onChange, variables, placeholder, onKeyDo
154
165
  spanRefs.current.set(index, el);
155
166
  else
156
167
  spanRefs.current.delete(index);
157
- }, className: "text-[#32D2E2]", children: token.text }, index)) : (_jsx("span", { children: token.text }, index)))) : (_jsx("span", { className: "text-muted", children: placeholder })) }), _jsx(Input, { ref: inputRef, id: id, variant: "plain", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": tooltip ? tooltipId : undefined, className: `relative w-full min-w-0 border-none bg-transparent px-2 py-1.5 text-[14px] text-transparent caret-text focus-visible:shadow-none ${className}`, type: "text", placeholder: placeholder, value: value, onChange: (e) => {
168
+ }, className: "text-[#32D2E2]", children: token.text }, index)) : (_jsx("span", { children: token.text }, index)))) : (_jsx("span", { className: "text-muted", children: placeholder })) }), _jsx(Input, { ref: inputRef, id: id, variant: "plain", role: source ? 'combobox' : undefined, "aria-autocomplete": source ? 'list' : undefined, "aria-expanded": source ? autocompleteOpen : undefined, "aria-controls": source && autocompleteOpen ? listboxId : undefined, "aria-activedescendant": source && autocompleteOpen && autocompleteActiveIndex >= 0
169
+ ? `${listboxId}-option-${autocompleteActiveIndex}`
170
+ : undefined, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": tooltip ? tooltipId : undefined, className: `relative w-full min-w-0 border-none bg-transparent px-2 py-1.5 text-[14px] text-transparent caret-text focus-visible:shadow-none ${className}`, type: "text", placeholder: placeholder, value: value, onChange: (e) => {
158
171
  onChange(e.target.value);
159
172
  queueMicrotask(updateTooltipFromCaret);
160
- }, onFocus: updateTooltipFromCaret, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp, onSelect: updateTooltipFromCaret, onClick: updateTooltipFromCaret, onScroll: syncScroll, onMouseMove: handleMouseMove, onMouseLeave: scheduleHide }), tooltip && tooltipContent && (_jsxs("div", { id: tooltipId, role: "tooltip", className: "pointer-events-auto fixed z-50 flex max-w-sm -translate-x-1/2 -translate-y-full flex-col gap-1.5 rounded-md border border-separator bg-surface px-3 py-2 text-[14px] text-text shadow-md", style: { top: tooltip.top - 4, left: tooltip.left }, onMouseEnter: cancelHide, onMouseLeave: () => setTooltip(null), children: [_jsx("span", { className: tooltipContent.muted ? 'text-muted' : undefined, children: tooltipContent.text }), onEditVariable && (_jsx("button", { type: "button", className: "self-start text-[14px] text-accent hover:underline app-no-drag", "aria-label": `Edit value for ${tooltip.key}`, onClick: () => {
173
+ }, onFocus: () => {
174
+ openAutocomplete();
175
+ updateTooltipFromCaret();
176
+ }, onBlur: closeAutocomplete, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp, onSelect: updateTooltipFromCaret, onClick: updateTooltipFromCaret, onScroll: syncScroll, onMouseMove: handleMouseMove, onMouseLeave: scheduleHide }), source && (_jsx(SuggestionList, { open: autocompleteOpen, items: autocompleteItems, activeIndex: autocompleteActiveIndex, anchorRef: inputRef, listboxId: listboxId, onSelect: selectItem, onActiveIndexChange: setActiveIndex, onClose: closeSuggestions })), tooltip && tooltipContent && (_jsxs("div", { id: tooltipId, role: "tooltip", className: "pointer-events-auto fixed z-50 flex max-w-sm -translate-x-1/2 -translate-y-full flex-col gap-1.5 rounded-md border border-separator bg-surface px-3 py-2 text-[14px] text-text shadow-md", style: { top: tooltip.top - 4, left: tooltip.left }, onMouseEnter: cancelHide, onMouseLeave: () => setTooltip(null), children: [_jsx("span", { className: tooltipContent.muted ? 'text-muted' : undefined, children: tooltipContent.text }), onEditVariable && (_jsx("button", { type: "button", className: "self-start text-[14px] text-accent hover:underline app-no-drag", "aria-label": `Edit value for ${tooltip.key}`, onClick: () => {
161
177
  onEditVariable();
162
178
  setTooltip(null);
163
179
  }, children: "Edit value" }))] }))] }));
@@ -1,4 +1,5 @@
1
1
  export { AsyncListState, ErrorRetry, LoadingMessage } from './AsyncListState/index.js';
2
+ export { AutocompleteInput, SuggestionList, useAutocomplete, type AutocompleteInputProps, type AutocompleteSource } from './Autocomplete/index.js';
2
3
  export { BusyIndicator } from './BusyIndicator/index.js';
3
4
  export type { Props as BusyIndicatorProps } from './BusyIndicator/index.js';
4
5
  export { BackButton } from './BackButton/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  export { AsyncListState, ErrorRetry, LoadingMessage } from './AsyncListState/index.js';
2
+ export { AutocompleteInput, SuggestionList, useAutocomplete } from './Autocomplete/index.js';
2
3
  export { BusyIndicator } from './BusyIndicator/index.js';
3
4
  export { BackButton } from './BackButton/index.js';
4
5
  export { Badge } from './Badge/index.js';
@@ -4,7 +4,8 @@ import type { ReactNode, ReactPortal } from 'react';
4
4
  * the sidebar or other overflow-hidden plugin webview containers.
5
5
  *
6
6
  * @param node - Modal element to render.
7
- * @returns Portal into document.body, or the node unchanged when document is unavailable.
7
+ * @returns Portal into document.body.
8
+ * @throws When `document` is unavailable (SSR, tests, or misconfigured host).
8
9
  */
9
10
  export declare function portalToBody(node: ReactNode): ReactPortal;
10
11
  //# sourceMappingURL=portalToBody.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"portalToBody.d.ts","sourceRoot":"","sources":["../../src/components/portalToBody.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEpD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,WAAW,CAKzD"}
1
+ {"version":3,"file":"portalToBody.d.ts","sourceRoot":"","sources":["../../src/components/portalToBody.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEpD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,WAAW,CAKzD"}
@@ -4,11 +4,12 @@ import { createPortal } from '@harborclient/sdk/react-dom';
4
4
  * the sidebar or other overflow-hidden plugin webview containers.
5
5
  *
6
6
  * @param node - Modal element to render.
7
- * @returns Portal into document.body, or the node unchanged when document is unavailable.
7
+ * @returns Portal into document.body.
8
+ * @throws When `document` is unavailable (SSR, tests, or misconfigured host).
8
9
  */
9
10
  export function portalToBody(node) {
10
- if (typeof document !== 'undefined') {
11
- return createPortal(node, document.body);
11
+ if (typeof document === 'undefined') {
12
+ throw new Error('portalToBody requires a DOM document');
12
13
  }
13
- return node;
14
+ return createPortal(node, document.body);
14
15
  }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export type * from './types';
1
+ export type * from './types.js';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAC"}
package/dist/main.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export type { CodeEditorSetup, CodeEditorTheme, Disposable, EchoServerIncomingRequest, EchoServerStartResult, EchoServerStatus, MainPluginContext, PluginHttp, PluginHttpRequest, PluginHttpResponse, PluginIpc, PluginScriptCollectionInit, PluginScriptContext, PluginScriptContextInit, PluginScriptEnvironmentInit, PluginScriptKeyValue, PluginScriptRequestInit, PluginScriptResponseInit, PluginScriptRunResult, PluginScripts, PluginScriptTestResult, PluginServer, PluginStorage, ScriptPhase } from './types';
1
+ export type { CodeEditorSetup, CodeEditorTheme, Disposable, EchoServerIncomingRequest, EchoServerStartResult, EchoServerStatus, MainPluginContext, PluginHttp, PluginHttpRequest, PluginHttpResponse, PluginIpc, PluginScriptCollectionInit, PluginScriptContext, PluginScriptContextInit, PluginScriptEnvironmentInit, PluginScriptKeyValue, PluginScriptRequestInit, PluginScriptResponseInit, PluginScriptRunResult, PluginScripts, PluginScriptTestResult, PluginServer, PluginStorage, ScriptPhase } from './types.js';
2
2
  //# sourceMappingURL=main.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,eAAe,EACf,UAAU,EACV,yBAAyB,EACzB,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,0BAA0B,EAC1B,mBAAmB,EACnB,uBAAuB,EACvB,2BAA2B,EAC3B,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,aAAa,EACb,sBAAsB,EACtB,YAAY,EACZ,aAAa,EACb,WAAW,EACZ,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,eAAe,EACf,UAAU,EACV,yBAAyB,EACzB,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,0BAA0B,EAC1B,mBAAmB,EACnB,uBAAuB,EACvB,2BAA2B,EAC3B,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,aAAa,EACb,sBAAsB,EACtB,YAAY,EACZ,aAAa,EACb,WAAW,EACZ,MAAM,YAAY,CAAC"}
@@ -1,6 +1,3 @@
1
- /** @type {number} */
2
- let nextRequestId = 1;
3
-
4
1
  /** @type {Map<number, { resolve: (value: unknown) => void; reject: (reason?: unknown) => void }>} */
5
2
  const pendingRequests = new Map();
6
3
 
@@ -1,5 +1,5 @@
1
1
  import type * as React from 'react';
2
- import type { Disposable, PluginContext, ThemeContribution } from '../types';
2
+ import type { Disposable, PluginContext, ThemeContribution } from '../types.js';
3
3
 
4
4
  /**
5
5
  * Installs the HarborClient renderer React instance for plugin JSX and hooks.
@@ -1,6 +1,6 @@
1
1
  import type * as React from 'react';
2
2
 
3
- export { Fragment } from './jsx-runtime';
3
+ export { Fragment } from './jsx-runtime.js';
4
4
 
5
5
  /**
6
6
  * Development JSX runtime entry; delegates to the production jsx helper.
@@ -41,7 +41,8 @@ export interface StorageStore<T> {
41
41
  */
42
42
  reloadFromStorage(): Promise<void>;
43
43
  /**
44
- * Updates the in-memory snapshot, notifies subscribers, and persists to storage.
44
+ * Persists to storage, then updates the in-memory snapshot and notifies subscribers.
45
+ * Rejects when persistence fails; the snapshot is left unchanged.
45
46
  *
46
47
  * @param next - New snapshot value.
47
48
  */
@@ -93,6 +94,10 @@ export declare function createExternalStore<T>(initial: T): {
93
94
  /**
94
95
  * Creates a storage-backed external store for sharing state across plugin webviews.
95
96
  *
97
+ * Hydrates from storage asynchronously on creation. Synchronous {@link StorageStore.getSnapshot}
98
+ * may return `parse(undefined)` until hydration completes; use {@link StorageStore.useValue}
99
+ * or await {@link StorageStore.reloadFromStorage} when you need the persisted value before reading.
100
+ *
96
101
  * Separate plugin webviews do not share memory; use {@link syncOnWindowFocus} to
97
102
  * reload when another surface writes storage.
98
103
  *
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/runtime/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAE5C;;;;;OAKG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAE5C;;OAEG;IACH,WAAW,IAAI,CAAC,CAAC;IAEjB;;OAEG;IACH,QAAQ,IAAI,CAAC,CAAC;IAEd;;OAEG;IACH,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB,CAAC,CAAC;IAC1C,4CAA4C;IAC5C,OAAO,EAAE,WAAW,CAAC;IAErB,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;;OAKG;IACH,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC,CAAC;IAE3B;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;IAEjC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG;IAClD,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;IAChD,WAAW,EAAE,MAAM,CAAC,CAAC;IACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;CAC7B,CAkBA;AAYD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,EAAE,yBAAyB,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CA+C5F;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,CAO1F;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,GAAG,YAAY,CAAC,OAAO,CAAC,EAAE,EACvD,OAAO,CAAC,EAAE,wBAAwB,GACjC,UAAU,CA0BZ"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/runtime/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAE5C;;;;;OAKG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAE5C;;OAEG;IACH,WAAW,IAAI,CAAC,CAAC;IAEjB;;OAEG;IACH,QAAQ,IAAI,CAAC,CAAC;IAEd;;OAEG;IACH,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC;;;;;OAKG;IACH,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB,CAAC,CAAC;IAC1C,4CAA4C;IAC5C,OAAO,EAAE,WAAW,CAAC;IAErB,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;;OAKG;IACH,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC,CAAC;IAE3B;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;IAEjC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG;IAClD,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;IAChD,WAAW,EAAE,MAAM,CAAC,CAAC;IACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;CAC7B,CAkBA;AAYD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,EAAE,yBAAyB,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAkD5F;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,CAO1F;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,GAAG,YAAY,CAAC,OAAO,CAAC,EAAE,EACvD,OAAO,CAAC,EAAE,wBAAwB,GACjC,UAAU,CA0BZ"}
@@ -35,6 +35,10 @@ function defaultEquals(a, b) {
35
35
  /**
36
36
  * Creates a storage-backed external store for sharing state across plugin webviews.
37
37
  *
38
+ * Hydrates from storage asynchronously on creation. Synchronous {@link StorageStore.getSnapshot}
39
+ * may return `parse(undefined)` until hydration completes; use {@link StorageStore.useValue}
40
+ * or await {@link StorageStore.reloadFromStorage} when you need the persisted value before reading.
41
+ *
38
42
  * Separate plugin webviews do not share memory; use {@link syncOnWindowFocus} to
39
43
  * reload when another surface writes storage.
40
44
  *
@@ -58,7 +62,8 @@ export function createStorageStore(options) {
58
62
  }
59
63
  }
60
64
  /**
61
- * Updates the snapshot, notifies subscribers, and persists when the value changed.
65
+ * Persists when the value changed, then updates the snapshot and notifies subscribers.
66
+ * Rejects when persistence fails; the snapshot is left unchanged.
62
67
  *
63
68
  * @param next - New snapshot value.
64
69
  */
@@ -67,8 +72,8 @@ export function createStorageStore(options) {
67
72
  if (equals(current, next)) {
68
73
  return;
69
74
  }
70
- external.setState(next);
71
75
  await storage.set(key, next);
76
+ external.setState(next);
72
77
  }
73
78
  /**
74
79
  * React hook returning the current storage-backed snapshot.
@@ -76,6 +81,7 @@ export function createStorageStore(options) {
76
81
  function useValue() {
77
82
  return useSyncExternalStore(external.subscribe, external.getSnapshot, external.getSnapshot);
78
83
  }
84
+ void reloadFromStorage();
79
85
  return {
80
86
  subscribe: external.subscribe,
81
87
  getSnapshot: external.getSnapshot,
@@ -49,7 +49,8 @@ export interface StorageStore<T> {
49
49
  reloadFromStorage(): Promise<void>;
50
50
 
51
51
  /**
52
- * Updates the in-memory snapshot, notifies subscribers, and persists to storage.
52
+ * Persists to storage, then updates the in-memory snapshot and notifies subscribers.
53
+ * Rejects when persistence fails; the snapshot is left unchanged.
53
54
  *
54
55
  * @param next - New snapshot value.
55
56
  */
@@ -137,6 +138,10 @@ function defaultEquals<T>(a: T, b: T): boolean {
137
138
  /**
138
139
  * Creates a storage-backed external store for sharing state across plugin webviews.
139
140
  *
141
+ * Hydrates from storage asynchronously on creation. Synchronous {@link StorageStore.getSnapshot}
142
+ * may return `parse(undefined)` until hydration completes; use {@link StorageStore.useValue}
143
+ * or await {@link StorageStore.reloadFromStorage} when you need the persisted value before reading.
144
+ *
140
145
  * Separate plugin webviews do not share memory; use {@link syncOnWindowFocus} to
141
146
  * reload when another surface writes storage.
142
147
  *
@@ -162,7 +167,8 @@ export function createStorageStore<T>(options: CreateStorageStoreOptions<T>): St
162
167
  }
163
168
 
164
169
  /**
165
- * Updates the snapshot, notifies subscribers, and persists when the value changed.
170
+ * Persists when the value changed, then updates the snapshot and notifies subscribers.
171
+ * Rejects when persistence fails; the snapshot is left unchanged.
166
172
  *
167
173
  * @param next - New snapshot value.
168
174
  */
@@ -171,8 +177,8 @@ export function createStorageStore<T>(options: CreateStorageStoreOptions<T>): St
171
177
  if (equals(current, next)) {
172
178
  return;
173
179
  }
174
- external.setState(next);
175
180
  await storage.set(key, next);
181
+ external.setState(next);
176
182
  }
177
183
 
178
184
  /**
@@ -182,6 +188,8 @@ export function createStorageStore<T>(options: CreateStorageStoreOptions<T>): St
182
188
  return useSyncExternalStore(external.subscribe, external.getSnapshot, external.getSnapshot);
183
189
  }
184
190
 
191
+ void reloadFromStorage();
192
+
185
193
  return {
186
194
  subscribe: external.subscribe,
187
195
  getSnapshot: external.getSnapshot,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harborclient/sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "TypeScript SDK for HarborClient plugin development.",
5
5
  "keywords": [
6
6
  "harborclient",
@@ -201,7 +201,8 @@
201
201
  "vitepress-plugin-mermaid": "^2.0.17"
202
202
  },
203
203
  "scripts": {
204
- "build": "tsc -p tsconfig.build.json && node scripts/copy-runtime.mjs",
204
+ "build": "tsc -p tsconfig.build.json && node scripts/copy-runtime.mjs && node scripts/assert-dist-publishable.mjs",
205
+ "attw": "attw --profile esm-only --no-summary",
205
206
  "format": "prettier --write .",
206
207
  "format:check": "prettier --check .",
207
208
  "lint": "eslint --cache .",
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=enhanceControl.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"enhanceControl.test.d.ts","sourceRoot":"","sources":["../../src/components/enhanceControl.test.ts"],"names":[],"mappings":""}
@@ -1,83 +0,0 @@
1
- import { describe, expect, it } from '@jest/globals';
2
- import { createElement, Fragment, isValidElement } from 'react';
3
- import { enhanceControl } from './enhanceControl.js';
4
- /** Stand-in for {@link Input} — function component that forwards props to a native input. */
5
- function TextInput(props) {
6
- return createElement('input', { type: 'text', ...props });
7
- }
8
- function propsOf(node) {
9
- if (!isValidElement(node)) {
10
- throw new Error('Expected a valid React element');
11
- }
12
- return node.props;
13
- }
14
- describe('enhanceControl', () => {
15
- it('merges aria-describedby and aria-invalid on a form component', () => {
16
- const result = enhanceControl(createElement(TextInput), {
17
- describedBy: 'field-error',
18
- invalid: true
19
- });
20
- expect(propsOf(result)['aria-describedby']).toBe('field-error');
21
- expect(propsOf(result)['aria-invalid']).toBe(true);
22
- });
23
- it('includes description id in aria-describedby without setting aria-invalid', () => {
24
- const result = enhanceControl(createElement(TextInput), {
25
- describedBy: 'api-key-description'
26
- });
27
- expect(propsOf(result)['aria-describedby']).toBe('api-key-description');
28
- expect(propsOf(result)['aria-invalid']).toBeUndefined();
29
- });
30
- it('merges description and error ids in aria-describedby', () => {
31
- const result = enhanceControl(createElement(TextInput), {
32
- describedBy: 'api-key-description api-key-error',
33
- invalid: true
34
- });
35
- expect(propsOf(result)['aria-describedby']).toBe('api-key-description api-key-error');
36
- expect(propsOf(result)['aria-invalid']).toBe(true);
37
- });
38
- it('merges with an existing aria-describedby value', () => {
39
- const result = enhanceControl(createElement(TextInput, { 'aria-describedby': 'existing' }), {
40
- describedBy: 'field-error'
41
- });
42
- expect(propsOf(result)['aria-describedby']).toBe('existing field-error');
43
- });
44
- it('enhances a control wrapped in a div', () => {
45
- const wrapped = createElement('div', null, createElement(TextInput));
46
- const result = enhanceControl(wrapped, { describedBy: 'field-error' });
47
- const inner = propsOf(result).children;
48
- expect(propsOf(inner)['aria-describedby']).toBe('field-error');
49
- });
50
- it('enhances a native input wrapped in a div', () => {
51
- const wrapped = createElement('div', null, createElement('input', { type: 'text' }));
52
- const result = enhanceControl(wrapped, { describedBy: 'field-error' });
53
- const inner = propsOf(result).children;
54
- expect(propsOf(inner)['aria-describedby']).toBe('field-error');
55
- });
56
- it('unwraps a single-element array', () => {
57
- const result = enhanceControl([createElement(TextInput)], { describedBy: 'field-error' });
58
- expect(propsOf(result)['aria-describedby']).toBe('field-error');
59
- });
60
- it('enhances a control wrapped in a fragment', () => {
61
- const wrapped = createElement(Fragment, null, createElement(TextInput));
62
- const result = enhanceControl(wrapped, { describedBy: 'field-error' });
63
- const inner = propsOf(result).children;
64
- expect(propsOf(inner)['aria-describedby']).toBe('field-error');
65
- });
66
- it('leaves multiple siblings unchanged', () => {
67
- const child = [createElement(TextInput), createElement('span')];
68
- const result = enhanceControl(child, { describedBy: 'field-error' });
69
- expect(result).toBe(child);
70
- });
71
- it('sets id when the control has none', () => {
72
- const result = enhanceControl(createElement('input', { type: 'checkbox' }), {
73
- id: 'auto-id'
74
- });
75
- expect(propsOf(result).id).toBe('auto-id');
76
- });
77
- it('preserves an existing id', () => {
78
- const result = enhanceControl(createElement('input', { id: 'existing-id', type: 'checkbox' }), {
79
- id: 'auto-id'
80
- });
81
- expect(propsOf(result).id).toBe('existing-id');
82
- });
83
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=utils.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../../src/components/utils.test.ts"],"names":[],"mappings":""}
@@ -1,44 +0,0 @@
1
- import { describe, expect, it } from '@jest/globals';
2
- import { resolveMenuTypeahead, resolveTabListKeyAction } from './utils.js';
3
- describe('resolveTabListKeyAction', () => {
4
- it('returns null for unhandled keys', () => {
5
- expect(resolveTabListKeyAction('Enter', 0, 3)).toBeNull();
6
- expect(resolveTabListKeyAction('Tab', 1, 3)).toBeNull();
7
- });
8
- it('returns null when itemCount is zero', () => {
9
- expect(resolveTabListKeyAction('ArrowRight', 0, 0)).toBeNull();
10
- });
11
- it('moves forward and backward with arrow keys', () => {
12
- expect(resolveTabListKeyAction('ArrowRight', 0, 3)).toBe(1);
13
- expect(resolveTabListKeyAction('ArrowDown', 1, 3)).toBe(2);
14
- expect(resolveTabListKeyAction('ArrowLeft', 0, 3)).toBe(2);
15
- expect(resolveTabListKeyAction('ArrowUp', 2, 3)).toBe(1);
16
- });
17
- it('jumps to first and last enabled items with Home and End', () => {
18
- expect(resolveTabListKeyAction('Home', 2, 3)).toBe(0);
19
- expect(resolveTabListKeyAction('End', 0, 3)).toBe(2);
20
- });
21
- it('skips disabled indices when navigating', () => {
22
- expect(resolveTabListKeyAction('ArrowRight', 0, 3, { disabledIndices: [1] })).toBe(2);
23
- expect(resolveTabListKeyAction('ArrowLeft', 2, 3, { disabledIndices: [1] })).toBe(0);
24
- expect(resolveTabListKeyAction('Home', 2, 3, { disabledIndices: [0] })).toBe(1);
25
- expect(resolveTabListKeyAction('End', 0, 3, { disabledIndices: [2] })).toBe(1);
26
- });
27
- });
28
- describe('resolveMenuTypeahead', () => {
29
- const labels = ['Rename', 'Duplicate', 'Delete'];
30
- it('returns null for non-printable keys', () => {
31
- expect(resolveMenuTypeahead(labels, 0, 'Enter', '')).toBeNull();
32
- expect(resolveMenuTypeahead(labels, 0, ' ', 'd')).toBeNull();
33
- });
34
- it('matches the next item whose label starts with the typed prefix', () => {
35
- expect(resolveMenuTypeahead(labels, 0, 'd', '')).toEqual({ index: 1, buffer: 'd' });
36
- expect(resolveMenuTypeahead(labels, 1, 'e', 'd')).toEqual({ index: 2, buffer: 'de' });
37
- });
38
- it('wraps the search from the current index', () => {
39
- expect(resolveMenuTypeahead(labels, 2, 'r', '')).toEqual({ index: 0, buffer: 'r' });
40
- });
41
- it('returns null when no label matches the prefix', () => {
42
- expect(resolveMenuTypeahead(labels, 0, 'z', '')).toBeNull();
43
- });
44
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=resolveRequest.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"resolveRequest.test.d.ts","sourceRoot":"","sources":["../../src/http/resolveRequest.test.ts"],"names":[],"mappings":""}
@@ -1,41 +0,0 @@
1
- import { describe, expect, it } from '@jest/globals';
2
- import { resolveRequest } from './resolveRequest.js';
3
- import { substituteVariables } from './substitute.js';
4
- describe('substituteVariables', () => {
5
- it('replaces known placeholders', () => {
6
- expect(substituteVariables('{{host}}/api', { host: 'https://example.com' })).toBe('https://example.com/api');
7
- });
8
- });
9
- describe('resolveRequest', () => {
10
- it('merges collection auth and variables', () => {
11
- const context = {
12
- draft: {
13
- method: 'POST',
14
- url: '{{base}}/users',
15
- params: [{ key: 'page', value: '1', enabled: true }],
16
- headers: [{ key: 'X-Test', value: '1', enabled: true }],
17
- body: '{"ok":true}',
18
- auth: {
19
- type: 'none',
20
- basic: { username: '', password: '' },
21
- bearer: { token: '' }
22
- },
23
- body_type: 'json'
24
- },
25
- response: null,
26
- readOnly: true,
27
- collectionAuth: {
28
- type: 'bearer',
29
- basic: { username: '', password: '' },
30
- bearer: { token: '{{token}}' }
31
- },
32
- collectionHeaders: [{ key: 'Accept', value: 'application/json', enabled: true }],
33
- variables: { base: 'https://api.test', token: 'secret' },
34
- requestKey: 'POST https://api.test/users'
35
- };
36
- const resolved = resolveRequest(context);
37
- expect(resolved.url).toBe('https://api.test/users?page=1');
38
- expect(resolved.headers.Authorization).toBe('Bearer secret');
39
- expect(resolved.headers['Content-Type']).toBe('application/json');
40
- });
41
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=substitute.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"substitute.test.d.ts","sourceRoot":"","sources":["../../src/http/substitute.test.ts"],"names":[],"mappings":""}