@harborclient/sdk 1.0.0 → 1.0.1

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 (60) 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/portalToBody.d.ts +2 -1
  5. package/dist/components/portalToBody.d.ts.map +1 -1
  6. package/dist/components/portalToBody.js +5 -4
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/main.d.ts +1 -1
  10. package/dist/main.d.ts.map +1 -1
  11. package/dist/runtime/index.d.ts +1 -1
  12. package/dist/runtime/jsx-dev-runtime.d.ts +1 -1
  13. package/dist/runtime/store.d.ts +6 -1
  14. package/dist/runtime/store.d.ts.map +1 -1
  15. package/dist/runtime/store.js +8 -2
  16. package/dist/runtime/store.ts +11 -3
  17. package/package.json +3 -2
  18. package/dist/components/enhanceControl.test.d.ts +0 -2
  19. package/dist/components/enhanceControl.test.d.ts.map +0 -1
  20. package/dist/components/enhanceControl.test.js +0 -83
  21. package/dist/components/utils.test.d.ts +0 -2
  22. package/dist/components/utils.test.d.ts.map +0 -1
  23. package/dist/components/utils.test.js +0 -44
  24. package/dist/http/resolveRequest.test.d.ts +0 -2
  25. package/dist/http/resolveRequest.test.d.ts.map +0 -1
  26. package/dist/http/resolveRequest.test.js +0 -41
  27. package/dist/http/substitute.test.d.ts +0 -2
  28. package/dist/http/substitute.test.d.ts.map +0 -1
  29. package/dist/http/substitute.test.js +0 -93
  30. package/dist/pluginDatabaseApi.test.d.ts +0 -2
  31. package/dist/pluginDatabaseApi.test.d.ts.map +0 -1
  32. package/dist/pluginDatabaseApi.test.js +0 -65
  33. package/dist/runtime/index.test.d.ts +0 -2
  34. package/dist/runtime/index.test.d.ts.map +0 -1
  35. package/dist/runtime/index.test.js +0 -41
  36. package/dist/runtime/index.test.ts +0 -53
  37. package/dist/runtime-utils.test.d.ts +0 -2
  38. package/dist/runtime-utils.test.d.ts.map +0 -1
  39. package/dist/runtime-utils.test.js +0 -177
  40. package/dist/signing/signing.test.d.ts +0 -2
  41. package/dist/signing/signing.test.d.ts.map +0 -1
  42. package/dist/signing/signing.test.js +0 -100
  43. package/dist/storage/cappedList.test.d.ts +0 -2
  44. package/dist/storage/cappedList.test.d.ts.map +0 -1
  45. package/dist/storage/cappedList.test.js +0 -11
  46. package/dist/storage/validate.test.d.ts +0 -2
  47. package/dist/storage/validate.test.d.ts.map +0 -1
  48. package/dist/storage/validate.test.js +0 -78
  49. package/dist/store.test.d.ts +0 -2
  50. package/dist/store.test.d.ts.map +0 -1
  51. package/dist/store.test.js +0 -248
  52. package/dist/utilities.test.d.ts +0 -2
  53. package/dist/utilities.test.d.ts.map +0 -1
  54. package/dist/utilities.test.js +0 -16
  55. package/dist/variables/dynamic.test.d.ts +0 -2
  56. package/dist/variables/dynamic.test.d.ts.map +0 -1
  57. package/dist/variables/dynamic.test.js +0 -60
  58. package/dist/variables/tokens.test.d.ts +0 -2
  59. package/dist/variables/tokens.test.d.ts.map +0 -1
  60. package/dist/variables/tokens.test.js +0 -160
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/build/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEpD;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gEAAgE;IAChE,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC;IACpC;;;OAGG;IACH,IAAI,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACjD;AAwCD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqDrF;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAiB7E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/build/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEpD;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gEAAgE;IAChE,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC;IACpC;;;OAGG;IACH,IAAI,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACjD;AAwCD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqDrF;AAYD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAiB7E"}
@@ -79,6 +79,15 @@ export async function buildRenderer(options = {}) {
79
79
  copyAssets(copy);
80
80
  }
81
81
  }
82
+ /**
83
+ * Escapes a string for use as a literal in a RegExp.
84
+ *
85
+ * @param value - Raw string to escape.
86
+ * @returns Regex-safe literal.
87
+ */
88
+ function escapeRegExp(value) {
89
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
90
+ }
82
91
  /**
83
92
  * esbuild plugin that stubs Node built-in modules with empty exports.
84
93
  *
@@ -93,7 +102,7 @@ export function nodeBuiltinStubsPlugin(moduleNames) {
93
102
  name: 'node-builtins-stub',
94
103
  setup(build) {
95
104
  for (const moduleName of moduleNames) {
96
- build.onResolve({ filter: new RegExp(`^${moduleName}$`) }, () => ({
105
+ build.onResolve({ filter: new RegExp(`^${escapeRegExp(moduleName)}$`) }, () => ({
97
106
  path: moduleName,
98
107
  namespace: 'node-stub'
99
108
  }));
package/dist/client.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export * from './index';
2
- export * from './runtime/index';
1
+ export * from './index.js';
2
+ export * from './runtime/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,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.1",
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":""}
@@ -1,93 +0,0 @@
1
- import { describe, expect, it } from '@jest/globals';
2
- import { resolveAuthVariables, substituteKeyValueRows, substituteVariables } from './substitute.js';
3
- describe('substituteVariables', () => {
4
- it('replaces known placeholders', () => {
5
- expect(substituteVariables('{{host}}/api', { host: 'https://example.com' })).toBe('https://example.com/api');
6
- });
7
- it('allows whitespace inside braces', () => {
8
- expect(substituteVariables('{{ host }}/api', { host: 'https://example.com' })).toBe('https://example.com/api');
9
- });
10
- it('supports dotted and hyphenated keys', () => {
11
- expect(substituteVariables('{{api.base}}-{{api-key}}', {
12
- 'api.base': 'https://example.com',
13
- 'api-key': 'secret'
14
- })).toBe('https://example.com-secret');
15
- });
16
- it('replaces multiple placeholders in one string', () => {
17
- expect(substituteVariables('{{scheme}}://{{host}}/{{path}}', {
18
- scheme: 'https',
19
- host: 'example.com',
20
- path: 'users'
21
- })).toBe('https://example.com/users');
22
- });
23
- it('leaves unknown placeholders unchanged', () => {
24
- expect(substituteVariables('{{missing}}/api', { host: 'https://example.com' })).toBe('{{missing}}/api');
25
- });
26
- it('replaces with empty string when variable is defined as empty', () => {
27
- expect(substituteVariables('prefix{{token}}suffix', { token: '' })).toBe('prefixsuffix');
28
- });
29
- it('returns text unchanged when there are no placeholders', () => {
30
- expect(substituteVariables('plain text', { host: 'https://example.com' })).toBe('plain text');
31
- });
32
- it('resolves dynamic variables when not in the runtime map', () => {
33
- const result = substituteVariables('prefix{{$timestamp}}suffix', {});
34
- expect(result).not.toContain('{{');
35
- expect(result).toMatch(/^prefix\d+suffix$/);
36
- });
37
- it('prefers runtime variables over dynamic variables', () => {
38
- expect(substituteVariables('{{$timestamp}}', { $timestamp: 'fixed' })).toBe('fixed');
39
- });
40
- });
41
- describe('resolveAuthVariables', () => {
42
- const auth = {
43
- type: 'basic',
44
- basic: {
45
- username: '{{user}}',
46
- password: '{{pass}}'
47
- },
48
- bearer: {
49
- token: '{{token}}'
50
- }
51
- };
52
- it('substitutes basic and bearer credential fields', () => {
53
- const substitute = (text) => text.replace('{{user}}', 'alice').replace('{{pass}}', 'secret').replace('{{token}}', 'tok');
54
- expect(resolveAuthVariables(auth, substitute)).toEqual({
55
- type: 'basic',
56
- basic: {
57
- username: 'alice',
58
- password: 'secret'
59
- },
60
- bearer: {
61
- token: 'tok'
62
- }
63
- });
64
- });
65
- it('preserves auth type and structure', () => {
66
- const substitute = (text) => text;
67
- const resolved = resolveAuthVariables(auth, substitute);
68
- expect(resolved.type).toBe('basic');
69
- expect(resolved.basic).toEqual(auth.basic);
70
- expect(resolved.bearer).toEqual(auth.bearer);
71
- });
72
- });
73
- describe('substituteKeyValueRows', () => {
74
- it('substitutes values while preserving row metadata', () => {
75
- const rows = [
76
- { key: 'Accept', value: '{{accept}}', enabled: true },
77
- { key: 'X-Api-Key', value: 'static', enabled: false }
78
- ];
79
- expect(substituteKeyValueRows(rows, { accept: 'application/json' })).toEqual([
80
- { key: 'Accept', value: 'application/json', enabled: true },
81
- { key: 'X-Api-Key', value: 'static', enabled: false }
82
- ]);
83
- });
84
- it('leaves unknown placeholders in row values', () => {
85
- const rows = [{ key: 'Host', value: '{{host}}', enabled: true }];
86
- expect(substituteKeyValueRows(rows, {})).toEqual([
87
- { key: 'Host', value: '{{host}}', enabled: true }
88
- ]);
89
- });
90
- it('returns an empty array for no rows', () => {
91
- expect(substituteKeyValueRows([], { host: 'example.com' })).toEqual([]);
92
- });
93
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=pluginDatabaseApi.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pluginDatabaseApi.test.d.ts","sourceRoot":"","sources":["../src/pluginDatabaseApi.test.ts"],"names":[],"mappings":""}
@@ -1,65 +0,0 @@
1
- import { describe, expect, it, jest } from '@jest/globals';
2
- import { createPluginDatabaseApi } from './runtime/pluginDatabaseApi.js';
3
- /**
4
- * Builds a mock database backend for unit tests.
5
- */
6
- function createMockBackend() {
7
- return {
8
- query: jest.fn(async () => undefined),
9
- exec: jest.fn(async () => undefined),
10
- beginTransaction: jest.fn(async () => 'txn-1'),
11
- endTransaction: jest.fn(async () => undefined)
12
- };
13
- }
14
- describe('createPluginDatabaseApi', () => {
15
- it('forwards get, all, and run with the correct query modes', async () => {
16
- const backend = createMockBackend();
17
- backend.query
18
- .mockResolvedValueOnce({ id: 1 })
19
- .mockResolvedValueOnce([{ id: 1 }, { id: 2 }])
20
- .mockResolvedValueOnce({ changes: 1, lastInsertRowid: 42 });
21
- const db = createPluginDatabaseApi(backend);
22
- await expect(db.get('SELECT 1', [1])).resolves.toEqual({ id: 1 });
23
- await expect(db.all('SELECT *', [2])).resolves.toEqual([{ id: 1 }, { id: 2 }]);
24
- await expect(db.run('INSERT INTO t VALUES (?)', [3])).resolves.toEqual({
25
- changes: 1,
26
- lastInsertRowid: 42
27
- });
28
- expect(backend.query).toHaveBeenNthCalledWith(1, 'get', 'SELECT 1', [1], undefined);
29
- expect(backend.query).toHaveBeenNthCalledWith(2, 'all', 'SELECT *', [2], undefined);
30
- expect(backend.query).toHaveBeenNthCalledWith(3, 'run', 'INSERT INTO t VALUES (?)', [3], undefined);
31
- });
32
- it('forwards exec unchanged', async () => {
33
- const backend = createMockBackend();
34
- const db = createPluginDatabaseApi(backend);
35
- await db.exec('CREATE TABLE t (id INTEGER)');
36
- expect(backend.exec).toHaveBeenCalledWith('CREATE TABLE t (id INTEGER)');
37
- });
38
- it('commits successful transactions and passes txnId to tx helpers', async () => {
39
- const backend = createMockBackend();
40
- backend.query.mockResolvedValue({ changes: 1, lastInsertRowid: 1 });
41
- const db = createPluginDatabaseApi(backend);
42
- const result = await db.transaction(async (tx) => {
43
- await tx.run('INSERT INTO t VALUES (?)', [1]);
44
- await tx.all('SELECT * FROM t');
45
- return 'done';
46
- });
47
- expect(result).toBe('done');
48
- expect(backend.beginTransaction).toHaveBeenCalledTimes(1);
49
- expect(backend.endTransaction).toHaveBeenCalledWith('txn-1', 'commit');
50
- expect(backend.query).toHaveBeenCalledWith('run', 'INSERT INTO t VALUES (?)', [1], 'txn-1');
51
- expect(backend.query).toHaveBeenCalledWith('all', 'SELECT * FROM t', undefined, 'txn-1');
52
- });
53
- it('rolls back failed transactions', async () => {
54
- const backend = createMockBackend();
55
- const db = createPluginDatabaseApi(backend);
56
- const boom = new Error('boom');
57
- await expect(db.transaction(async (tx) => {
58
- await tx.run('INSERT INTO t VALUES (?)', [1]);
59
- throw boom;
60
- })).rejects.toThrow(boom);
61
- expect(backend.beginTransaction).toHaveBeenCalledTimes(1);
62
- expect(backend.endTransaction).toHaveBeenCalledWith('txn-1', 'rollback');
63
- expect(backend.endTransaction).not.toHaveBeenCalledWith('txn-1', 'commit');
64
- });
65
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=index.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/runtime/index.test.ts"],"names":[],"mappings":""}