@harborclient/sdk 0.6.15 → 0.6.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build/index.d.ts +59 -0
- package/dist/build/index.d.ts.map +1 -0
- package/dist/build/index.js +107 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/portalToBody.d.ts +10 -0
- package/dist/components/portalToBody.d.ts.map +1 -0
- package/dist/components/portalToBody.js +14 -0
- package/dist/eslint/index.d.ts +9 -0
- package/dist/eslint/index.d.ts.map +1 -0
- package/dist/eslint/index.js +18 -0
- package/dist/http/resolveRequest.test.js +2 -1
- package/dist/pluginDatabaseApi.test.d.ts +2 -0
- package/dist/pluginDatabaseApi.test.d.ts.map +1 -0
- package/dist/pluginDatabaseApi.test.js +65 -0
- package/dist/runtime/createBridgedPluginContext.js +67 -3
- package/dist/runtime/index.d.ts +18 -0
- package/dist/runtime/index.js +23 -0
- package/dist/runtime/index.test.d.ts +2 -0
- package/dist/runtime/index.test.d.ts.map +1 -0
- package/dist/runtime/index.test.js +41 -0
- package/dist/runtime/index.test.ts +53 -0
- package/dist/runtime/jsx-runtime-host.d.ts +44 -0
- package/dist/runtime/jsx-runtime-host.js +42 -0
- package/dist/runtime/pluginDatabaseApi.d.ts +36 -0
- package/dist/runtime/pluginDatabaseApi.js +52 -0
- package/dist/runtime/react-dom.d.ts +1 -0
- package/dist/runtime/react-dom.js +16 -0
- package/dist/runtime/react.d.ts +6 -0
- package/dist/runtime/react.js +70 -2
- package/dist/runtime/reactHost.js +52 -0
- package/dist/runtime/store.d.ts +101 -0
- package/dist/runtime/store.d.ts.map +1 -1
- package/dist/runtime/store.js +94 -0
- package/dist/runtime/store.ts +203 -0
- package/dist/runtime/viewHost.js +2 -1
- package/dist/runtime-utils.d.ts +59 -0
- package/dist/runtime-utils.d.ts.map +1 -1
- package/dist/runtime-utils.js +66 -0
- package/dist/runtime-utils.test.js +74 -1
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +1 -0
- package/dist/storage/validate.d.ts +71 -0
- package/dist/storage/validate.d.ts.map +1 -0
- package/dist/storage/validate.js +111 -0
- package/dist/storage/validate.test.d.ts +2 -0
- package/dist/storage/validate.test.d.ts.map +1 -0
- package/dist/storage/validate.test.js +78 -0
- package/dist/store.test.d.ts +2 -0
- package/dist/store.test.d.ts.map +1 -0
- package/dist/store.test.js +248 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +35 -7
- package/tsconfig.base.json +15 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { BuildOptions, Plugin } from 'esbuild';
|
|
2
|
+
/**
|
|
3
|
+
* Which JSX runtime alias (if any) the renderer bundle should use.
|
|
4
|
+
*
|
|
5
|
+
* - `host` — bind `react/jsx-runtime` to `@harborclient/sdk/jsx-runtime-host` for
|
|
6
|
+
* bundled third-party React libraries that import jsx-runtime at module load.
|
|
7
|
+
* - `runtime` — route through the SDK runtime shims that forward to `hc.react`.
|
|
8
|
+
* - `automatic` — automatic JSX with `@harborclient/sdk` import source, no alias.
|
|
9
|
+
* - `none` — no JSX options (theme plugins that only register colors).
|
|
10
|
+
*/
|
|
11
|
+
export type JsxRuntimeMode = 'host' | 'runtime' | 'automatic' | 'none';
|
|
12
|
+
/**
|
|
13
|
+
* Options for {@link buildRenderer}.
|
|
14
|
+
*/
|
|
15
|
+
export interface BuildRendererOptions {
|
|
16
|
+
/** Renderer entry file. Defaults to `src/renderer.tsx`. */
|
|
17
|
+
entry?: string;
|
|
18
|
+
/** Output bundle path. Defaults to `dist/renderer.js`. */
|
|
19
|
+
outfile?: string;
|
|
20
|
+
/** When true, watch for changes instead of a one-shot build. */
|
|
21
|
+
watch?: boolean;
|
|
22
|
+
/** JSX runtime wiring for the bundle. Defaults to `runtime`. */
|
|
23
|
+
jsxRuntime?: JsxRuntimeMode;
|
|
24
|
+
/** Modules left external (provided by the HarborClient host). */
|
|
25
|
+
external?: string[];
|
|
26
|
+
/** esbuild `define` replacements merged into the build. */
|
|
27
|
+
define?: Record<string, string>;
|
|
28
|
+
/** Extra esbuild plugins appended after built-in plugins. */
|
|
29
|
+
plugins?: Plugin[];
|
|
30
|
+
/**
|
|
31
|
+
* When set, alias `react` to this path instead of externalizing it (e.g. aws-sigv
|
|
32
|
+
* proxy shim). Implies `external: []` unless `external` is explicitly provided.
|
|
33
|
+
*/
|
|
34
|
+
aliasReactTo?: string;
|
|
35
|
+
/** esbuild log level. Defaults to `info`. */
|
|
36
|
+
logLevel?: BuildOptions['logLevel'];
|
|
37
|
+
/**
|
|
38
|
+
* Files to copy after a successful build, e.g. theme stylesheets.
|
|
39
|
+
* Each pair is `[source, destination]`.
|
|
40
|
+
*/
|
|
41
|
+
copy?: ReadonlyArray<readonly [string, string]>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Bundles a HarborClient plugin renderer entry with canonical esbuild options.
|
|
45
|
+
*
|
|
46
|
+
* @param options - Build configuration.
|
|
47
|
+
*/
|
|
48
|
+
export declare function buildRenderer(options?: BuildRendererOptions): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* esbuild plugin that stubs Node built-in modules with empty exports.
|
|
51
|
+
*
|
|
52
|
+
* Use when a dependency imports Node modules that are unused at runtime in the
|
|
53
|
+
* renderer (e.g. `dotenv` importing `fs`/`path`).
|
|
54
|
+
*
|
|
55
|
+
* @param moduleNames - Built-in module names to stub (e.g. `path`, `fs`).
|
|
56
|
+
* @returns esbuild plugin.
|
|
57
|
+
*/
|
|
58
|
+
export declare function nodeBuiltinStubsPlugin(moduleNames: readonly string[]): Plugin;
|
|
59
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { cpSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
import * as esbuild from 'esbuild';
|
|
4
|
+
const JSX_RUNTIME_HOST = '@harborclient/sdk/jsx-runtime-host';
|
|
5
|
+
const JSX_RUNTIME = '@harborclient/sdk/jsx-runtime';
|
|
6
|
+
const JSX_DEV_RUNTIME = '@harborclient/sdk/jsx-dev-runtime';
|
|
7
|
+
/**
|
|
8
|
+
* Builds the jsx-runtime alias map for the given mode.
|
|
9
|
+
*
|
|
10
|
+
* @param mode - JSX runtime wiring mode.
|
|
11
|
+
* @returns Alias entries to merge into esbuild options, or undefined when none apply.
|
|
12
|
+
*/
|
|
13
|
+
function jsxRuntimeAliases(mode) {
|
|
14
|
+
if (mode === 'host') {
|
|
15
|
+
return {
|
|
16
|
+
'react/jsx-runtime': JSX_RUNTIME_HOST,
|
|
17
|
+
'react/jsx-dev-runtime': JSX_RUNTIME_HOST
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (mode === 'runtime') {
|
|
21
|
+
return {
|
|
22
|
+
'react/jsx-runtime': JSX_RUNTIME,
|
|
23
|
+
'react/jsx-dev-runtime': JSX_DEV_RUNTIME
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Copies asset files after a successful renderer build.
|
|
30
|
+
*
|
|
31
|
+
* @param pairs - Source/destination path pairs.
|
|
32
|
+
*/
|
|
33
|
+
function copyAssets(pairs) {
|
|
34
|
+
for (const [source, destination] of pairs) {
|
|
35
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
36
|
+
cpSync(source, destination);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Bundles a HarborClient plugin renderer entry with canonical esbuild options.
|
|
41
|
+
*
|
|
42
|
+
* @param options - Build configuration.
|
|
43
|
+
*/
|
|
44
|
+
export async function buildRenderer(options = {}) {
|
|
45
|
+
const { entry = 'src/renderer.tsx', outfile = 'dist/renderer.js', watch = false, jsxRuntime = 'runtime', define, plugins = [], aliasReactTo, logLevel = 'info', copy = [] } = options;
|
|
46
|
+
const external = options.external ?? (aliasReactTo ? [] : ['react', 'react-dom']);
|
|
47
|
+
const alias = {
|
|
48
|
+
...jsxRuntimeAliases(jsxRuntime),
|
|
49
|
+
...(aliasReactTo ? { react: aliasReactTo } : {})
|
|
50
|
+
};
|
|
51
|
+
const shared = {
|
|
52
|
+
entryPoints: [entry],
|
|
53
|
+
outfile,
|
|
54
|
+
bundle: true,
|
|
55
|
+
format: 'esm',
|
|
56
|
+
platform: 'browser',
|
|
57
|
+
logLevel,
|
|
58
|
+
external,
|
|
59
|
+
define,
|
|
60
|
+
plugins,
|
|
61
|
+
...(Object.keys(alias).length > 0 ? { alias } : {}),
|
|
62
|
+
...(jsxRuntime === 'none'
|
|
63
|
+
? {}
|
|
64
|
+
: {
|
|
65
|
+
jsx: 'automatic',
|
|
66
|
+
jsxImportSource: '@harborclient/sdk'
|
|
67
|
+
})
|
|
68
|
+
};
|
|
69
|
+
if (watch) {
|
|
70
|
+
const context = await esbuild.context(shared);
|
|
71
|
+
await context.watch();
|
|
72
|
+
console.log('Watching for changes…');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const context = await esbuild.context(shared);
|
|
76
|
+
await context.rebuild();
|
|
77
|
+
await context.dispose();
|
|
78
|
+
if (copy.length > 0) {
|
|
79
|
+
copyAssets(copy);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* esbuild plugin that stubs Node built-in modules with empty exports.
|
|
84
|
+
*
|
|
85
|
+
* Use when a dependency imports Node modules that are unused at runtime in the
|
|
86
|
+
* renderer (e.g. `dotenv` importing `fs`/`path`).
|
|
87
|
+
*
|
|
88
|
+
* @param moduleNames - Built-in module names to stub (e.g. `path`, `fs`).
|
|
89
|
+
* @returns esbuild plugin.
|
|
90
|
+
*/
|
|
91
|
+
export function nodeBuiltinStubsPlugin(moduleNames) {
|
|
92
|
+
return {
|
|
93
|
+
name: 'node-builtins-stub',
|
|
94
|
+
setup(build) {
|
|
95
|
+
for (const moduleName of moduleNames) {
|
|
96
|
+
build.onResolve({ filter: new RegExp(`^${moduleName}$`) }, () => ({
|
|
97
|
+
path: moduleName,
|
|
98
|
+
namespace: 'node-stub'
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
build.onLoad({ filter: /.*/, namespace: 'node-stub' }, () => ({
|
|
102
|
+
contents: 'export default {};',
|
|
103
|
+
loader: 'js'
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
@@ -24,6 +24,7 @@ export type { Props as KeyValueEditorProps } from './KeyValueEditor/index.js';
|
|
|
24
24
|
export { MethodSelect } from './MethodSelect/index.js';
|
|
25
25
|
export { Modal, ModalFooter, ModalFormLayout } from './Modal/index.js';
|
|
26
26
|
export { ModalHeader } from './Modal/ModalHeader.js';
|
|
27
|
+
export { portalToBody } from './portalToBody.js';
|
|
27
28
|
export { OverlayCloseButton } from './OverlayCloseButton/index.js';
|
|
28
29
|
export { Page } from './Page/index.js';
|
|
29
30
|
export { PageHeader } from './PageHeader/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":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,YAAY,EAAE,KAAK,IAAI,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,0BAA0B,EAC3B,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,YAAY,EAAE,KAAK,IAAI,eAAe,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,YAAY,EAAE,KAAK,IAAI,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EACL,KAAK,EACL,QAAQ,EACR,KAAK,EACL,MAAM,EACN,QAAQ,EACR,KAAK,EACL,UAAU,EACV,YAAY,EACZ,iBAAiB,EAClB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,YAAY,EAAE,KAAK,IAAI,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC7E,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAC3F,OAAO,EACL,cAAc,EACd,UAAU,EACV,oBAAoB,EACpB,+BAA+B,EAC/B,qBAAqB,EACtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,YAAY,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAChG,YAAY,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EACL,KAAK,EACL,SAAS,EACT,SAAS,EACT,SAAS,EACT,WAAW,EACX,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACpB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,YAAY,EAAE,KAAK,IAAI,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACrE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/components/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export { KeyValueEditor } from './KeyValueEditor/index.js';
|
|
|
17
17
|
export { MethodSelect } from './MethodSelect/index.js';
|
|
18
18
|
export { Modal, ModalFooter, ModalFormLayout } from './Modal/index.js';
|
|
19
19
|
export { ModalHeader } from './Modal/ModalHeader.js';
|
|
20
|
+
export { portalToBody } from './portalToBody.js';
|
|
20
21
|
export { OverlayCloseButton } from './OverlayCloseButton/index.js';
|
|
21
22
|
export { Page } from './Page/index.js';
|
|
22
23
|
export { PageHeader } from './PageHeader/index.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactNode, ReactPortal } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Portals modal content to document.body so fixed positioning is not clipped by
|
|
4
|
+
* the sidebar or other overflow-hidden plugin webview containers.
|
|
5
|
+
*
|
|
6
|
+
* @param node - Modal element to render.
|
|
7
|
+
* @returns Portal into document.body, or the node unchanged when document is unavailable.
|
|
8
|
+
*/
|
|
9
|
+
export declare function portalToBody(node: ReactNode): ReactPortal;
|
|
10
|
+
//# sourceMappingURL=portalToBody.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createPortal } from '@harborclient/sdk/react-dom';
|
|
2
|
+
/**
|
|
3
|
+
* Portals modal content to document.body so fixed positioning is not clipped by
|
|
4
|
+
* the sidebar or other overflow-hidden plugin webview containers.
|
|
5
|
+
*
|
|
6
|
+
* @param node - Modal element to render.
|
|
7
|
+
* @returns Portal into document.body, or the node unchanged when document is unavailable.
|
|
8
|
+
*/
|
|
9
|
+
export function portalToBody(node) {
|
|
10
|
+
if (typeof document !== 'undefined') {
|
|
11
|
+
return createPortal(node, document.body);
|
|
12
|
+
}
|
|
13
|
+
return node;
|
|
14
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared flat ESLint config for HarborClient plugins (ESLint 10 compatible).
|
|
3
|
+
*
|
|
4
|
+
* Plugins re-export this from `eslint.config.mjs`:
|
|
5
|
+
* `export { default } from '@harborclient/sdk/eslint';`
|
|
6
|
+
*/
|
|
7
|
+
declare const _default: import("typescript-eslint").FlatConfig.ConfigArray;
|
|
8
|
+
export default _default;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/eslint/index.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;;AACH,wBAaE"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import eslint from '@eslint/js';
|
|
2
|
+
import eslintConfigPrettier from 'eslint-config-prettier';
|
|
3
|
+
import globals from 'globals';
|
|
4
|
+
import tseslint from 'typescript-eslint';
|
|
5
|
+
/**
|
|
6
|
+
* Shared flat ESLint config for HarborClient plugins (ESLint 10 compatible).
|
|
7
|
+
*
|
|
8
|
+
* Plugins re-export this from `eslint.config.mjs`:
|
|
9
|
+
* `export { default } from '@harborclient/sdk/eslint';`
|
|
10
|
+
*/
|
|
11
|
+
export default tseslint.config({
|
|
12
|
+
ignores: ['**/node_modules', '**/dist', 'pnpm-lock.yaml']
|
|
13
|
+
}, eslint.configs.recommended, tseslint.configs.recommended, {
|
|
14
|
+
files: ['**/*.mjs'],
|
|
15
|
+
languageOptions: {
|
|
16
|
+
globals: globals.node
|
|
17
|
+
}
|
|
18
|
+
}, eslintConfigPrettier);
|
|
@@ -30,7 +30,8 @@ describe('resolveRequest', () => {
|
|
|
30
30
|
bearer: { token: '{{token}}' }
|
|
31
31
|
},
|
|
32
32
|
collectionHeaders: [{ key: 'Accept', value: 'application/json', enabled: true }],
|
|
33
|
-
variables: { base: 'https://api.test', token: 'secret' }
|
|
33
|
+
variables: { base: 'https://api.test', token: 'secret' },
|
|
34
|
+
requestKey: 'POST https://api.test/users'
|
|
34
35
|
};
|
|
35
36
|
const resolved = resolveRequest(context);
|
|
36
37
|
expect(resolved.url).toBe('https://api.test/users?page=1');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pluginDatabaseApi.test.d.ts","sourceRoot":"","sources":["../src/pluginDatabaseApi.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
});
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
registerContributionHeaderActions,
|
|
8
8
|
registerContributionIndicator
|
|
9
9
|
} from './contributionRegistry.js';
|
|
10
|
+
import { createPluginDatabaseApi } from './pluginDatabaseApi.js';
|
|
10
11
|
import { setHostReact } from './reactHost.js';
|
|
11
12
|
|
|
12
13
|
/** @type {Map<string, Set<(...args: unknown[]) => void | Promise<void>>>} */
|
|
@@ -197,7 +198,7 @@ export function createBridgedPluginContext({ pluginId, mode, contributionId, rea
|
|
|
197
198
|
await bridgeInvoke('storage.set', { key, value });
|
|
198
199
|
}
|
|
199
200
|
},
|
|
200
|
-
database: {
|
|
201
|
+
database: createPluginDatabaseApi({
|
|
201
202
|
query: (mode, sql, params, txnId) => {
|
|
202
203
|
assertPermission('database');
|
|
203
204
|
return bridgeInvoke('database.query', { mode, sql, params, txnId });
|
|
@@ -214,7 +215,7 @@ export function createBridgedPluginContext({ pluginId, mode, contributionId, rea
|
|
|
214
215
|
assertPermission('database');
|
|
215
216
|
return bridgeInvoke('database.endTransaction', { txnId, action });
|
|
216
217
|
}
|
|
217
|
-
},
|
|
218
|
+
}),
|
|
218
219
|
fs: {
|
|
219
220
|
pickFile: async (options) => {
|
|
220
221
|
assertPermission('filesystem:pick');
|
|
@@ -691,7 +692,7 @@ export function mountContributionView({
|
|
|
691
692
|
'mainViews'
|
|
692
693
|
]);
|
|
693
694
|
|
|
694
|
-
if (FILL_SURFACE_KINDS.has(kind)) {
|
|
695
|
+
if (FILL_SURFACE_KINDS.has(kind) && slot === 'content') {
|
|
695
696
|
document.body.classList.add('plugin-surface-fill');
|
|
696
697
|
}
|
|
697
698
|
|
|
@@ -704,6 +705,15 @@ export function mountContributionView({
|
|
|
704
705
|
root.style.overflow = 'hidden';
|
|
705
706
|
}
|
|
706
707
|
|
|
708
|
+
if (slot === 'indicator') {
|
|
709
|
+
document.body.classList.add('plugin-surface-indicator');
|
|
710
|
+
document.documentElement.classList.add('plugin-surface-indicator');
|
|
711
|
+
root.style.display = 'inline-flex';
|
|
712
|
+
root.style.width = 'fit-content';
|
|
713
|
+
root.style.maxWidth = '100%';
|
|
714
|
+
root.style.overflow = 'hidden';
|
|
715
|
+
}
|
|
716
|
+
|
|
707
717
|
/** @type {ResizeObserver | null} */
|
|
708
718
|
let resizeObserver = null;
|
|
709
719
|
/** @type {number | null} */
|
|
@@ -827,6 +837,60 @@ export function mountContributionView({
|
|
|
827
837
|
};
|
|
828
838
|
}
|
|
829
839
|
|
|
840
|
+
if (slot === 'indicator') {
|
|
841
|
+
/**
|
|
842
|
+
* Reports footer panel indicator size so the host webview stays compact inline.
|
|
843
|
+
*/
|
|
844
|
+
const reportIndicatorSize = () => {
|
|
845
|
+
const measureTarget = root.firstElementChild ?? root;
|
|
846
|
+
const width = Math.ceil(
|
|
847
|
+
Math.max(
|
|
848
|
+
measureTarget.scrollWidth,
|
|
849
|
+
measureTarget.getBoundingClientRect().width,
|
|
850
|
+
measureTarget.offsetWidth
|
|
851
|
+
)
|
|
852
|
+
);
|
|
853
|
+
const height = Math.ceil(
|
|
854
|
+
Math.max(
|
|
855
|
+
measureTarget.scrollHeight,
|
|
856
|
+
measureTarget.getBoundingClientRect().height,
|
|
857
|
+
measureTarget.offsetHeight
|
|
858
|
+
)
|
|
859
|
+
);
|
|
860
|
+
if (width <= 0 && height <= 0) {
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
if (resizeFrame != null) {
|
|
864
|
+
cancelAnimationFrame(resizeFrame);
|
|
865
|
+
}
|
|
866
|
+
resizeFrame = requestAnimationFrame(() => {
|
|
867
|
+
resizeFrame = requestAnimationFrame(() => {
|
|
868
|
+
resizeFrame = null;
|
|
869
|
+
void bridgeInvoke('view.reportSize', {
|
|
870
|
+
...(width > 0 ? { width } : {}),
|
|
871
|
+
...(height > 0 ? { height } : {}),
|
|
872
|
+
slot: 'indicator'
|
|
873
|
+
});
|
|
874
|
+
});
|
|
875
|
+
});
|
|
876
|
+
};
|
|
877
|
+
|
|
878
|
+
resizeObserver = new ResizeObserver(() => {
|
|
879
|
+
reportIndicatorSize();
|
|
880
|
+
});
|
|
881
|
+
resizeObserver.observe(root);
|
|
882
|
+
|
|
883
|
+
render();
|
|
884
|
+
reportIndicatorSize();
|
|
885
|
+
|
|
886
|
+
return () => {
|
|
887
|
+
resizeObserver?.disconnect();
|
|
888
|
+
if (resizeFrame != null) {
|
|
889
|
+
cancelAnimationFrame(resizeFrame);
|
|
890
|
+
}
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
|
|
830
894
|
const unsubscribe = bridgeOn('view.context', (payload) => {
|
|
831
895
|
currentContext = payload;
|
|
832
896
|
render();
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type * as React from 'react';
|
|
2
|
+
import type { Disposable, PluginContext, ThemeContribution } from '../types';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Installs the HarborClient renderer React instance for plugin JSX and hooks.
|
|
@@ -18,3 +19,20 @@ export function installReact(react: typeof React): void;
|
|
|
18
19
|
export function createPluginComponent<P extends Record<string, unknown>>(
|
|
19
20
|
factory: (react: typeof React) => React.ComponentType<P>
|
|
20
21
|
): React.ComponentType<P>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Registers a custom appearance theme and tracks its disposable for cleanup.
|
|
25
|
+
*
|
|
26
|
+
* @param hc - Renderer plugin context.
|
|
27
|
+
* @param theme - Theme definition; `theme.id` must match `contributes.themes`.
|
|
28
|
+
* @returns Disposable that unregisters the theme.
|
|
29
|
+
*/
|
|
30
|
+
export function registerTheme(hc: PluginContext, theme: ThemeContribution): Disposable;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Identity helper that applies `ThemeContribution` typing to a theme literal.
|
|
34
|
+
*
|
|
35
|
+
* @param theme - Theme definition.
|
|
36
|
+
* @returns The same theme, typed.
|
|
37
|
+
*/
|
|
38
|
+
export function defineTheme(theme: ThemeContribution): ThemeContribution;
|
package/dist/runtime/index.js
CHANGED
|
@@ -41,3 +41,26 @@ export function createPluginComponent(factory) {
|
|
|
41
41
|
|
|
42
42
|
return PluginComponent;
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Registers a custom appearance theme and tracks its disposable for cleanup.
|
|
47
|
+
*
|
|
48
|
+
* @param {import('../types').PluginContext} hc - Renderer plugin context.
|
|
49
|
+
* @param {import('../types').ThemeContribution} theme - Theme definition; `theme.id` must match `contributes.themes`.
|
|
50
|
+
* @returns {import('../types').Disposable} Disposable that unregisters the theme.
|
|
51
|
+
*/
|
|
52
|
+
export function registerTheme(hc, theme) {
|
|
53
|
+
const disposable = hc.themes.register(theme);
|
|
54
|
+
hc.subscriptions.push(disposable);
|
|
55
|
+
return disposable;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Identity helper that applies `ThemeContribution` typing to a theme literal.
|
|
60
|
+
*
|
|
61
|
+
* @param {import('../types').ThemeContribution} theme - Theme definition.
|
|
62
|
+
* @returns {import('../types').ThemeContribution} The same theme, typed.
|
|
63
|
+
*/
|
|
64
|
+
export function defineTheme(theme) {
|
|
65
|
+
return theme;
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/runtime/index.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, expect, it, jest } from '@jest/globals';
|
|
2
|
+
import { defineTheme, registerTheme } from './index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Builds a minimal plugin context mock for registerTheme tests.
|
|
5
|
+
*/
|
|
6
|
+
function createMockPluginContext() {
|
|
7
|
+
const disposable = { dispose: jest.fn() };
|
|
8
|
+
const registerMock = jest.fn(() => disposable);
|
|
9
|
+
const subscriptions = [];
|
|
10
|
+
return {
|
|
11
|
+
subscriptions,
|
|
12
|
+
themes: { register: registerMock },
|
|
13
|
+
registerMock
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
describe('registerTheme', () => {
|
|
17
|
+
it('registers the theme, pushes the disposable onto subscriptions, and returns it', () => {
|
|
18
|
+
const hc = createMockPluginContext();
|
|
19
|
+
const theme = {
|
|
20
|
+
id: 'solarized',
|
|
21
|
+
title: 'Solarized Dark',
|
|
22
|
+
type: 'dark',
|
|
23
|
+
colors: { surface: '#002b36' }
|
|
24
|
+
};
|
|
25
|
+
const disposable = registerTheme(hc, theme);
|
|
26
|
+
expect(hc.registerMock).toHaveBeenCalledWith(theme);
|
|
27
|
+
expect(hc.subscriptions).toHaveLength(1);
|
|
28
|
+
expect(hc.subscriptions[0]).toBe(disposable);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe('defineTheme', () => {
|
|
32
|
+
it('returns the theme argument unchanged', () => {
|
|
33
|
+
const theme = {
|
|
34
|
+
id: 'nord',
|
|
35
|
+
title: 'Nord',
|
|
36
|
+
type: 'dark',
|
|
37
|
+
stylesheet: 'dist/theme.css'
|
|
38
|
+
};
|
|
39
|
+
expect(defineTheme(theme)).toBe(theme);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, expect, it, jest } from '@jest/globals';
|
|
2
|
+
import type { PluginContext, ThemeContribution } from '../types';
|
|
3
|
+
import { defineTheme, registerTheme } from './index.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Builds a minimal plugin context mock for registerTheme tests.
|
|
7
|
+
*/
|
|
8
|
+
function createMockPluginContext(): PluginContext & {
|
|
9
|
+
registerMock: jest.MockedFunction<PluginContext['themes']['register']>;
|
|
10
|
+
} {
|
|
11
|
+
const disposable = { dispose: jest.fn() };
|
|
12
|
+
const registerMock = jest.fn(() => disposable);
|
|
13
|
+
const subscriptions: PluginContext['subscriptions'] = [];
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
subscriptions,
|
|
17
|
+
themes: { register: registerMock },
|
|
18
|
+
registerMock
|
|
19
|
+
} as unknown as PluginContext & {
|
|
20
|
+
registerMock: jest.MockedFunction<PluginContext['themes']['register']>;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe('registerTheme', () => {
|
|
25
|
+
it('registers the theme, pushes the disposable onto subscriptions, and returns it', () => {
|
|
26
|
+
const hc = createMockPluginContext();
|
|
27
|
+
const theme: ThemeContribution = {
|
|
28
|
+
id: 'solarized',
|
|
29
|
+
title: 'Solarized Dark',
|
|
30
|
+
type: 'dark',
|
|
31
|
+
colors: { surface: '#002b36' }
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const disposable = registerTheme(hc, theme);
|
|
35
|
+
|
|
36
|
+
expect(hc.registerMock).toHaveBeenCalledWith(theme);
|
|
37
|
+
expect(hc.subscriptions).toHaveLength(1);
|
|
38
|
+
expect(hc.subscriptions[0]).toBe(disposable);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('defineTheme', () => {
|
|
43
|
+
it('returns the theme argument unchanged', () => {
|
|
44
|
+
const theme: ThemeContribution = {
|
|
45
|
+
id: 'nord',
|
|
46
|
+
title: 'Nord',
|
|
47
|
+
type: 'dark',
|
|
48
|
+
stylesheet: 'dist/theme.css'
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
expect(defineTheme(theme)).toBe(theme);
|
|
52
|
+
});
|
|
53
|
+
});
|