@harperfast/harper 5.2.0-alpha.6 → 5.2.0-beta.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.
- package/bin/cliOperations.ts +76 -12
- package/bin/run.ts +10 -0
- package/bin/status.ts +1 -1
- package/components/Application.ts +146 -83
- package/components/Scope.ts +4 -0
- package/components/componentLoader.ts +7 -0
- package/components/operations.js +21 -1
- package/config/configUtils.ts +139 -5
- package/config-app.schema.json +70 -0
- package/dataLayer/harperBridge/ResourceBridge.ts +7 -0
- package/dist/bin/cliOperations.js +76 -12
- package/dist/bin/cliOperations.js.map +1 -1
- package/dist/bin/run.js +9 -0
- package/dist/bin/run.js.map +1 -1
- package/dist/bin/status.js +1 -1
- package/dist/bin/status.js.map +1 -1
- package/dist/components/Application.d.ts +12 -5
- package/dist/components/Application.js +121 -60
- package/dist/components/Application.js.map +1 -1
- package/dist/components/Scope.d.ts +1 -0
- package/dist/components/Scope.js +4 -0
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/componentLoader.js +7 -0
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/operations.js +20 -1
- package/dist/components/operations.js.map +1 -1
- package/dist/config/configUtils.d.ts +31 -0
- package/dist/config/configUtils.js +127 -5
- package/dist/config/configUtils.js.map +1 -1
- package/dist/dataLayer/harperBridge/ResourceBridge.js +8 -0
- package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/resources/DatabaseTransaction.d.ts +12 -0
- package/dist/resources/DatabaseTransaction.js +97 -0
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/RequestTarget.js +13 -3
- package/dist/resources/RequestTarget.js.map +1 -1
- package/dist/resources/Resource.js +21 -2
- package/dist/resources/Resource.js.map +1 -1
- package/dist/resources/Table.d.ts +3 -1
- package/dist/resources/Table.js +48 -5
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/analytics/metadata.d.ts +3 -0
- package/dist/resources/analytics/metadata.js +3 -0
- package/dist/resources/analytics/metadata.js.map +1 -1
- package/dist/resources/analytics/write.js +22 -0
- package/dist/resources/analytics/write.js.map +1 -1
- package/dist/resources/databases.js +21 -0
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/defineResource.js +20 -7
- package/dist/resources/defineResource.js.map +1 -1
- package/dist/resources/jsResource.d.ts +24 -0
- package/dist/resources/jsResource.js +58 -2
- package/dist/resources/jsResource.js.map +1 -1
- package/dist/resources/openApi.js +45 -20
- package/dist/resources/openApi.js.map +1 -1
- package/dist/resources/scheduler/CronExpression.d.ts +71 -0
- package/dist/resources/scheduler/CronExpression.js +367 -0
- package/dist/resources/scheduler/CronExpression.js.map +1 -0
- package/dist/resources/scheduler/engine.d.ts +91 -0
- package/dist/resources/scheduler/engine.js +767 -0
- package/dist/resources/scheduler/engine.js.map +1 -0
- package/dist/resources/scheduler/scheduler.d.ts +33 -0
- package/dist/resources/scheduler/scheduler.js +200 -0
- package/dist/resources/scheduler/scheduler.js.map +1 -0
- package/dist/security/auth.js +1 -0
- package/dist/security/auth.js.map +1 -1
- package/dist/security/jsLoader.js +8 -0
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/security/keys.d.ts +32 -0
- package/dist/security/keys.js +147 -0
- package/dist/security/keys.js.map +1 -1
- package/dist/server/REST.js +67 -1
- package/dist/server/REST.js.map +1 -1
- package/dist/server/Server.d.ts +6 -0
- package/dist/server/Server.js.map +1 -1
- package/dist/server/http.d.ts +2 -0
- package/dist/server/http.js +139 -14
- package/dist/server/http.js.map +1 -1
- package/dist/server/operationsServer.js +3 -3
- package/dist/server/operationsServer.js.map +1 -1
- package/dist/server/serverHelpers/progressEmitter.js +5 -1
- package/dist/server/serverHelpers/progressEmitter.js.map +1 -1
- package/dist/server/threads/threadServer.js +9 -5
- package/dist/server/threads/threadServer.js.map +1 -1
- package/dist/sqlEngine/binder/bind.d.ts +72 -0
- package/dist/sqlEngine/binder/bind.js +289 -0
- package/dist/sqlEngine/binder/bind.js.map +1 -0
- package/dist/sqlEngine/config.d.ts +35 -0
- package/dist/sqlEngine/config.js +62 -0
- package/dist/sqlEngine/config.js.map +1 -0
- package/dist/sqlEngine/diff/differential.d.ts +23 -0
- package/dist/sqlEngine/diff/differential.js +90 -0
- package/dist/sqlEngine/diff/differential.js.map +1 -0
- package/dist/sqlEngine/errors.d.ts +22 -0
- package/dist/sqlEngine/errors.js +36 -0
- package/dist/sqlEngine/errors.js.map +1 -0
- package/dist/sqlEngine/executor/runMutation.d.ts +35 -0
- package/dist/sqlEngine/executor/runMutation.js +352 -0
- package/dist/sqlEngine/executor/runMutation.js.map +1 -0
- package/dist/sqlEngine/executor/runSelect.d.ts +7 -0
- package/dist/sqlEngine/executor/runSelect.js +14 -0
- package/dist/sqlEngine/executor/runSelect.js.map +1 -0
- package/dist/sqlEngine/expressions/compile.d.ts +24 -0
- package/dist/sqlEngine/expressions/compile.js +326 -0
- package/dist/sqlEngine/expressions/compile.js.map +1 -0
- package/dist/sqlEngine/functions/aggregates.d.ts +12 -0
- package/dist/sqlEngine/functions/aggregates.js +299 -0
- package/dist/sqlEngine/functions/aggregates.js.map +1 -0
- package/dist/sqlEngine/functions/registry.d.ts +36 -0
- package/dist/sqlEngine/functions/registry.js +33 -0
- package/dist/sqlEngine/functions/registry.js.map +1 -0
- package/dist/sqlEngine/functions/standard.d.ts +9 -0
- package/dist/sqlEngine/functions/standard.js +86 -0
- package/dist/sqlEngine/functions/standard.js.map +1 -0
- package/dist/sqlEngine/index.d.ts +29 -0
- package/dist/sqlEngine/index.js +62 -0
- package/dist/sqlEngine/index.js.map +1 -0
- package/dist/sqlEngine/logical/build.d.ts +20 -0
- package/dist/sqlEngine/logical/build.js +326 -0
- package/dist/sqlEngine/logical/build.js.map +1 -0
- package/dist/sqlEngine/logical/op.d.ts +105 -0
- package/dist/sqlEngine/logical/op.js +10 -0
- package/dist/sqlEngine/logical/op.js.map +1 -0
- package/dist/sqlEngine/optimizer/joinAnalysis.d.ts +37 -0
- package/dist/sqlEngine/optimizer/joinAnalysis.js +144 -0
- package/dist/sqlEngine/optimizer/joinAnalysis.js.map +1 -0
- package/dist/sqlEngine/optimizer/optimize.d.ts +13 -0
- package/dist/sqlEngine/optimizer/optimize.js +53 -0
- package/dist/sqlEngine/optimizer/optimize.js.map +1 -0
- package/dist/sqlEngine/optimizer/ruleEngine.d.ts +7 -0
- package/dist/sqlEngine/optimizer/ruleEngine.js +26 -0
- package/dist/sqlEngine/optimizer/ruleEngine.js.map +1 -0
- package/dist/sqlEngine/optimizer/rules/limitPushdown.d.ts +19 -0
- package/dist/sqlEngine/optimizer/rules/limitPushdown.js +55 -0
- package/dist/sqlEngine/optimizer/rules/limitPushdown.js.map +1 -0
- package/dist/sqlEngine/optimizer/rules/planJoins.d.ts +19 -0
- package/dist/sqlEngine/optimizer/rules/planJoins.js +62 -0
- package/dist/sqlEngine/optimizer/rules/planJoins.js.map +1 -0
- package/dist/sqlEngine/optimizer/rules/predicateNormalize.d.ts +18 -0
- package/dist/sqlEngine/optimizer/rules/predicateNormalize.js +80 -0
- package/dist/sqlEngine/optimizer/rules/predicateNormalize.js.map +1 -0
- package/dist/sqlEngine/optimizer/rules/predicatePushdown.d.ts +11 -0
- package/dist/sqlEngine/optimizer/rules/predicatePushdown.js +48 -0
- package/dist/sqlEngine/optimizer/rules/predicatePushdown.js.map +1 -0
- package/dist/sqlEngine/optimizer/rules/projectionPushdown.d.ts +23 -0
- package/dist/sqlEngine/optimizer/rules/projectionPushdown.js +160 -0
- package/dist/sqlEngine/optimizer/rules/projectionPushdown.js.map +1 -0
- package/dist/sqlEngine/optimizer/rules/sortFromIndex.d.ts +14 -0
- package/dist/sqlEngine/optimizer/rules/sortFromIndex.js +49 -0
- package/dist/sqlEngine/optimizer/rules/sortFromIndex.js.map +1 -0
- package/dist/sqlEngine/optimizer/rules/validateScannable.d.ts +15 -0
- package/dist/sqlEngine/optimizer/rules/validateScannable.js +60 -0
- package/dist/sqlEngine/optimizer/rules/validateScannable.js.map +1 -0
- package/dist/sqlEngine/optimizer/whereToConditions.d.ts +85 -0
- package/dist/sqlEngine/optimizer/whereToConditions.js +484 -0
- package/dist/sqlEngine/optimizer/whereToConditions.js.map +1 -0
- package/dist/sqlEngine/parser/ast.d.ts +139 -0
- package/dist/sqlEngine/parser/ast.js +10 -0
- package/dist/sqlEngine/parser/ast.js.map +1 -0
- package/dist/sqlEngine/parser/normalizer.d.ts +28 -0
- package/dist/sqlEngine/parser/normalizer.js +413 -0
- package/dist/sqlEngine/parser/normalizer.js.map +1 -0
- package/dist/sqlEngine/parser/parse.d.ts +14 -0
- package/dist/sqlEngine/parser/parse.js +20 -0
- package/dist/sqlEngine/parser/parse.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalDistinct.d.ts +9 -0
- package/dist/sqlEngine/physical/PhysicalDistinct.js +29 -0
- package/dist/sqlEngine/physical/PhysicalDistinct.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalFilter.d.ts +10 -0
- package/dist/sqlEngine/physical/PhysicalFilter.js +25 -0
- package/dist/sqlEngine/physical/PhysicalFilter.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalHashAggregate.d.ts +27 -0
- package/dist/sqlEngine/physical/PhysicalHashAggregate.js +100 -0
- package/dist/sqlEngine/physical/PhysicalHashAggregate.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalHashJoin.d.ts +29 -0
- package/dist/sqlEngine/physical/PhysicalHashJoin.js +93 -0
- package/dist/sqlEngine/physical/PhysicalHashJoin.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalIndexNestedLoopJoin.d.ts +36 -0
- package/dist/sqlEngine/physical/PhysicalIndexNestedLoopJoin.js +78 -0
- package/dist/sqlEngine/physical/PhysicalIndexNestedLoopJoin.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalIndexScan.d.ts +22 -0
- package/dist/sqlEngine/physical/PhysicalIndexScan.js +77 -0
- package/dist/sqlEngine/physical/PhysicalIndexScan.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalLimit.d.ts +5 -0
- package/dist/sqlEngine/physical/PhysicalLimit.js +28 -0
- package/dist/sqlEngine/physical/PhysicalLimit.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalNestedLoopJoin.d.ts +20 -0
- package/dist/sqlEngine/physical/PhysicalNestedLoopJoin.js +51 -0
- package/dist/sqlEngine/physical/PhysicalNestedLoopJoin.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalProject.d.ts +18 -0
- package/dist/sqlEngine/physical/PhysicalProject.js +117 -0
- package/dist/sqlEngine/physical/PhysicalProject.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalQualify.d.ts +13 -0
- package/dist/sqlEngine/physical/PhysicalQualify.js +29 -0
- package/dist/sqlEngine/physical/PhysicalQualify.js.map +1 -0
- package/dist/sqlEngine/physical/PhysicalSort.d.ts +12 -0
- package/dist/sqlEngine/physical/PhysicalSort.js +59 -0
- package/dist/sqlEngine/physical/PhysicalSort.js.map +1 -0
- package/dist/sqlEngine/physical/op.d.ts +11 -0
- package/dist/sqlEngine/physical/op.js +9 -0
- package/dist/sqlEngine/physical/op.js.map +1 -0
- package/dist/sqlEngine/physical/plan.d.ts +23 -0
- package/dist/sqlEngine/physical/plan.js +162 -0
- package/dist/sqlEngine/physical/plan.js.map +1 -0
- package/dist/sqlEngine/router.d.ts +26 -0
- package/dist/sqlEngine/router.js +48 -0
- package/dist/sqlEngine/router.js.map +1 -0
- package/dist/sqlEngine/types.d.ts +37 -0
- package/dist/sqlEngine/types.js +13 -0
- package/dist/sqlEngine/types.js.map +1 -0
- package/dist/sqlTranslator/index.js +7 -1
- package/dist/sqlTranslator/index.js.map +1 -1
- package/dist/utility/common_utils.js +25 -0
- package/dist/utility/common_utils.js.map +1 -1
- package/dist/utility/install/installer.d.ts +9 -1
- package/dist/utility/install/installer.js +21 -0
- package/dist/utility/install/installer.js.map +1 -1
- package/dist/validation/configValidator.js +3 -0
- package/dist/validation/configValidator.js.map +1 -1
- package/dist/validation/deleteValidator.js +10 -2
- package/dist/validation/deleteValidator.js.map +1 -1
- package/npm-shrinkwrap.json +272 -230
- package/package.json +3 -3
- package/resources/DESIGN.md +17 -16
- package/resources/DatabaseTransaction.ts +95 -0
- package/resources/RequestTarget.ts +12 -3
- package/resources/Resource.ts +23 -2
- package/resources/Table.ts +57 -6
- package/resources/analytics/metadata.ts +3 -0
- package/resources/analytics/write.ts +23 -0
- package/resources/databases.ts +24 -0
- package/resources/defineResource.ts +17 -4
- package/resources/jsResource.ts +61 -2
- package/resources/openApi.ts +44 -19
- package/resources/scheduler/CronExpression.ts +394 -0
- package/resources/scheduler/engine.ts +812 -0
- package/resources/scheduler/scheduler.ts +236 -0
- package/security/auth.ts +1 -0
- package/security/jsLoader.ts +8 -0
- package/security/keys.ts +152 -0
- package/server/REST.ts +70 -1
- package/server/Server.ts +6 -0
- package/server/http.ts +122 -15
- package/server/operationsServer.ts +5 -3
- package/server/serverHelpers/progressEmitter.ts +5 -1
- package/server/threads/threadServer.js +9 -5
- package/sqlTranslator/index.ts +16 -6
- package/studio/web/assets/{Chat-CTjtL8Z4.js → Chat-DHP4XpID.js} +2 -2
- package/studio/web/assets/{Chat-CTjtL8Z4.js.map → Chat-DHP4XpID.js.map} +1 -1
- package/studio/web/assets/{FloatingChat-CafHR4Ur.js → FloatingChat-CJ7PssCv.js} +4 -4
- package/studio/web/assets/{FloatingChat-CafHR4Ur.js.map → FloatingChat-CJ7PssCv.js.map} +1 -1
- package/studio/web/assets/{applications-Buh_q0Vj.js → applications-DxXiGpsR.js} +2 -2
- package/studio/web/assets/{applications-Buh_q0Vj.js.map → applications-DxXiGpsR.js.map} +1 -1
- package/studio/web/assets/{index-0hXeECkS.js → index-BdbBanDP.js} +6 -6
- package/studio/web/assets/{index-0hXeECkS.js.map → index-BdbBanDP.js.map} +1 -1
- package/studio/web/assets/{index.lazy-B00B7VBT.js → index.lazy-B2eH28zD.js} +4 -4
- package/studio/web/assets/{index.lazy-B00B7VBT.js.map → index.lazy-B2eH28zD.js.map} +1 -1
- package/studio/web/assets/{profile-Cg2wwYPn.js → profile-DK5hgucv.js} +2 -2
- package/studio/web/assets/{profile-Cg2wwYPn.js.map → profile-DK5hgucv.js.map} +1 -1
- package/studio/web/assets/{setComponentFile-DCaDIvyB.js → setComponentFile-BVDWRYxx.js} +2 -2
- package/studio/web/assets/{setComponentFile-DCaDIvyB.js.map → setComponentFile-BVDWRYxx.js.map} +1 -1
- package/studio/web/assets/{setup-CAVcAQjK.js → setup-DJ9BInoK.js} +2 -2
- package/studio/web/assets/{setup-CAVcAQjK.js.map → setup-DJ9BInoK.js.map} +1 -1
- package/studio/web/assets/{status-BRXorNdD.js → status-B_qzmgfD.js} +2 -2
- package/studio/web/assets/{status-BRXorNdD.js.map → status-B_qzmgfD.js.map} +1 -1
- package/studio/web/assets/{swagger-ui-react-Dy1D62vO.js → swagger-ui-react-DOL5jCqg.js} +2 -2
- package/studio/web/assets/{swagger-ui-react-Dy1D62vO.js.map → swagger-ui-react-DOL5jCqg.js.map} +1 -1
- package/studio/web/assets/{tsMode-A8gbL74v.js → tsMode-DpxUxfTW.js} +2 -2
- package/studio/web/assets/{tsMode-A8gbL74v.js.map → tsMode-DpxUxfTW.js.map} +1 -1
- package/studio/web/assets/{useEntityRestURL-cDodrVcQ.js → useEntityRestURL-CU_lY6XW.js} +2 -2
- package/studio/web/assets/{useEntityRestURL-cDodrVcQ.js.map → useEntityRestURL-CU_lY6XW.js.map} +1 -1
- package/studio/web/index.html +1 -1
- package/utility/common_utils.ts +26 -0
- package/utility/install/installer.ts +26 -1
- package/validation/configValidator.ts +3 -0
- package/validation/deleteValidator.ts +11 -2
package/server/http.ts
CHANGED
|
@@ -12,10 +12,10 @@ import * as env from '../utility/environment/environmentManager.ts';
|
|
|
12
12
|
import * as terms from '../utility/hdbTerms.ts';
|
|
13
13
|
import { getConfigPath } from '../config/configUtils.ts';
|
|
14
14
|
import { getTicketKeys, getWorkerIndex } from './threads/manageThreads.js';
|
|
15
|
-
import { createTLSSelector } from '../security/keys.ts';
|
|
15
|
+
import { createTLSSelector, getEffectiveTlsCiphers } from '../security/keys.ts';
|
|
16
16
|
import { createSecureServer, createServer as createH2CServer } from 'node:http2';
|
|
17
17
|
import { createServer as createSecureServerHttp1 } from 'node:https';
|
|
18
|
-
import { createServer, IncomingMessage } from 'node:http';
|
|
18
|
+
import { createServer, IncomingMessage, validateHeaderName, validateHeaderValue } from 'node:http';
|
|
19
19
|
import { createServer as createNetServer } from 'node:net';
|
|
20
20
|
import { Request, BunRequest, UwsRequest, isBun } from './serverHelpers/Request.ts';
|
|
21
21
|
import { appendHeader, Headers, toWriteHeadHeaders } from './serverHelpers/Headers.ts';
|
|
@@ -136,11 +136,65 @@ export function cleanupSocketsDirectory() {
|
|
|
136
136
|
} catch {}
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
// Entries in `universalHeaders` that were pushed by `applySecurityHeaders`, so a config
|
|
140
|
+
// hot-reload can remove exactly the entries it owns without clobbering entries pushed by
|
|
141
|
+
// other components.
|
|
142
|
+
let ownedSecurityHeaders: [string, string][] = [];
|
|
143
|
+
|
|
144
|
+
/** Validate and apply `http.securityHeaders` config into `universalHeaders`, replacing any entries owned by a prior call. */
|
|
145
|
+
function applySecurityHeaders(securityHeaders: HttpOptions['securityHeaders']) {
|
|
146
|
+
for (const entry of ownedSecurityHeaders) {
|
|
147
|
+
const index = universalHeaders.indexOf(entry);
|
|
148
|
+
if (index !== -1) universalHeaders.splice(index, 1);
|
|
149
|
+
}
|
|
150
|
+
ownedSecurityHeaders = [];
|
|
151
|
+
if (!securityHeaders) return;
|
|
152
|
+
if (typeof securityHeaders !== 'object' || Array.isArray(securityHeaders)) {
|
|
153
|
+
harperLogger.error('Invalid http.securityHeaders value: expected a map of header names to values');
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
for (const [name, rawValue] of Object.entries(securityHeaders)) {
|
|
157
|
+
const value = '' + rawValue;
|
|
158
|
+
try {
|
|
159
|
+
validateHeaderName(name);
|
|
160
|
+
validateHeaderValue(name, value);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
harperLogger.error(`Invalid http.securityHeaders entry "${name}": ${errorToString(error)}`);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const entry: [string, string] = [name, value];
|
|
166
|
+
ownedSecurityHeaders.push(entry);
|
|
167
|
+
universalHeaders.push(entry);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Merge `universalHeaders` into `headers` as defaults: a header the app already set (matched by name) is never overridden (app-wins precedence). */
|
|
172
|
+
function applyUniversalHeaders(headers: { has(name: string): boolean; set(name: string, value: string): void }) {
|
|
173
|
+
for (const [key, value] of universalHeaders) {
|
|
174
|
+
if (!headers.has(key)) headers.set(key, value);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Only the first http scope to load (the root config) owns securityHeaders: 'http' is a
|
|
179
|
+
// trusted plugin key, so an application config.yaml with an `http:` block re-invokes
|
|
180
|
+
// handleApplication, and without this guard that call would wipe root-configured headers.
|
|
181
|
+
let securityHeadersOwned = false;
|
|
182
|
+
|
|
183
|
+
/** Test seam: reset the module-level guard so tests can re-invoke handleApplication. */
|
|
184
|
+
export function _resetSecurityHeadersOwnedForTest(): void {
|
|
185
|
+
applySecurityHeaders(undefined);
|
|
186
|
+
securityHeadersOwned = false;
|
|
187
|
+
}
|
|
188
|
+
|
|
139
189
|
export function handleApplication(scope: Scope) {
|
|
140
190
|
httpOptions = scope.options.getAll() as HttpOptions;
|
|
191
|
+
const ownsSecurityHeaders = !securityHeadersOwned;
|
|
192
|
+
securityHeadersOwned = true;
|
|
193
|
+
if (ownsSecurityHeaders) applySecurityHeaders(httpOptions.securityHeaders);
|
|
141
194
|
scope.options.on('change', (_key) => {
|
|
142
195
|
// TODO: Check to see if the key is something we can or can't handle
|
|
143
196
|
httpOptions = scope.options.getAll() as HttpOptions;
|
|
197
|
+
if (ownsSecurityHeaders) applySecurityHeaders(httpOptions.securityHeaders);
|
|
144
198
|
});
|
|
145
199
|
}
|
|
146
200
|
export function getHttpOptions() {
|
|
@@ -420,7 +474,6 @@ function getHTTPServer(port: number, secure: boolean, options: ServerOptions) {
|
|
|
420
474
|
let http2;
|
|
421
475
|
|
|
422
476
|
if (secure) {
|
|
423
|
-
const tlsConfig = env.get('tls');
|
|
424
477
|
// check if we want to enable HTTP/2; operations server doesn't use HTTP/2 because it doesn't allow the
|
|
425
478
|
// ALPNCallback to work with our custom protocol for replication
|
|
426
479
|
http2 = env.get(serverPrefix + '_http2');
|
|
@@ -435,7 +488,9 @@ function getHTTPServer(port: number, secure: boolean, options: ServerOptions) {
|
|
|
435
488
|
requestCert: Boolean(mtls || isMtls),
|
|
436
489
|
ticketKeys: getTicketKeys(),
|
|
437
490
|
SNICallback: createTLSSelector(usageType ?? 'server', mtls),
|
|
438
|
-
|
|
491
|
+
// the listener-level cipher string is the only one OpenSSL honors (SNI contexts can't
|
|
492
|
+
// carry their own), so resolve it from every configured source
|
|
493
|
+
ciphers: getEffectiveTlsCiphers(usageType ?? 'server', mtls || isMtls),
|
|
439
494
|
});
|
|
440
495
|
}
|
|
441
496
|
const requestHandler = async (nodeRequest: IncomingMessage, nodeResponse: any) => {
|
|
@@ -459,9 +514,7 @@ function getHTTPServer(port: number, secure: boolean, options: ServerOptions) {
|
|
|
459
514
|
if (!response.headers?.set) {
|
|
460
515
|
response.headers = new Headers(response.headers);
|
|
461
516
|
}
|
|
462
|
-
|
|
463
|
-
response.headers.set(key, value);
|
|
464
|
-
}
|
|
517
|
+
if (universalHeaders.length > 0) applyUniversalHeaders(response.headers);
|
|
465
518
|
if (response.status === -1) {
|
|
466
519
|
// This means the HDB stack didn't handle the request, and we can then cascade the request
|
|
467
520
|
// to the server-level handler, forming the bridge to the slower legacy fastify framework that expects
|
|
@@ -532,6 +585,16 @@ function getHTTPServer(port: number, secure: boolean, options: ServerOptions) {
|
|
|
532
585
|
else nodeResponse.writeHead(status, toWriteHeadHeaders(headers));
|
|
533
586
|
}
|
|
534
587
|
if (sentBody) nodeResponse.end(body);
|
|
588
|
+
} else if (universalHeaders.length > 0 && !nodeResponse.headersSent) {
|
|
589
|
+
// Known limitation: a handler that synchronously calls writeHead before
|
|
590
|
+
// returning { handlesHeaders: true } has already sent headers and cannot
|
|
591
|
+
// receive universal headers (no in-tree component does this).
|
|
592
|
+
// handlesHeaders responses (e.g. static's send() stream) write their own headers
|
|
593
|
+
// directly to nodeResponse; pre-set universal headers as defaults — a header the
|
|
594
|
+
// stream sets itself (same name) will overwrite these
|
|
595
|
+
for (const [key, value] of universalHeaders) {
|
|
596
|
+
if (!nodeResponse.hasHeader(key)) nodeResponse.setHeader(key, value);
|
|
597
|
+
}
|
|
535
598
|
}
|
|
536
599
|
const handlerPath = request.handlerPath;
|
|
537
600
|
const method = request.method;
|
|
@@ -573,6 +636,13 @@ function getHTTPServer(port: number, secure: boolean, options: ServerOptions) {
|
|
|
573
636
|
const statusCode = error.statusCode ?? error.status;
|
|
574
637
|
const status = statusCode || 500;
|
|
575
638
|
try {
|
|
639
|
+
if (universalHeaders.length > 0 && !nodeResponse.headersSent) {
|
|
640
|
+
// universal headers apply to error responses too; writeHead's explicit
|
|
641
|
+
// headers take precedence, so error-provided headers still win
|
|
642
|
+
for (const [key, value] of universalHeaders) {
|
|
643
|
+
if (!nodeResponse.hasHeader(key)) nodeResponse.setHeader(key, value);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
576
646
|
nodeResponse.writeHead(status, toWriteHeadHeaders(headers));
|
|
577
647
|
} catch {} // silently ignore errors writing headers, because they may have been set already
|
|
578
648
|
nodeResponse.end(errorToString(error));
|
|
@@ -619,6 +689,8 @@ function getHTTPServer(port: number, secure: boolean, options: ServerOptions) {
|
|
|
619
689
|
if (secure) {
|
|
620
690
|
if (!server.ports) server.ports = [];
|
|
621
691
|
server.ports.push(port);
|
|
692
|
+
server.appliedCiphers = options.ciphers ?? null;
|
|
693
|
+
server.verifiesClientCerts = Boolean(mtls || isMtls);
|
|
622
694
|
options.SNICallback.initialize(server);
|
|
623
695
|
if (mtls) server.mtlsConfig = mtls;
|
|
624
696
|
server.on('secureConnection', (socket) => {
|
|
@@ -734,11 +806,12 @@ function makeUwsHandler(port: number | string, isOperationsServer: boolean, requ
|
|
|
734
806
|
if (!response) response = unhandled(request);
|
|
735
807
|
let headers = response.headers;
|
|
736
808
|
if (!headers?.set) headers = new Headers(headers);
|
|
737
|
-
for (const [key, value] of universalHeaders) headers.set(key, value);
|
|
738
809
|
if (response.status === -1) {
|
|
739
810
|
// The chain didn't handle it. If a Fastify fallback is registered for this port (legacy
|
|
740
811
|
// custom-function routes via server.http(fastify.server)), delegate to it via inject(),
|
|
741
|
-
// mirroring the Bun path; otherwise it's a genuine 404.
|
|
812
|
+
// mirroring the Bun path; otherwise it's a genuine 404. Neither branch below reuses
|
|
813
|
+
// `headers` above (both build a fresh Headers from the fallback response), so universal
|
|
814
|
+
// headers must be (re-)applied on whichever Headers object actually gets returned.
|
|
742
815
|
const fastify = fastifyInstances[port];
|
|
743
816
|
if (fastify) {
|
|
744
817
|
const injectResult = await injectToFastify(fastify, {
|
|
@@ -756,6 +829,7 @@ function makeUwsHandler(port: number | string, isOperationsServer: boolean, requ
|
|
|
756
829
|
if (Array.isArray(v)) respHeaders.set(k, k.toLowerCase() === 'set-cookie' ? v : v.join(', '));
|
|
757
830
|
else respHeaders.set(k, String(v));
|
|
758
831
|
}
|
|
832
|
+
if (universalHeaders.length > 0) applyUniversalHeaders(respHeaders);
|
|
759
833
|
logHttpRequest(request, injectResult.statusCode, requestId, performance.now() - startTime);
|
|
760
834
|
const responseStream = injectResult.stream();
|
|
761
835
|
// Event-stream (SSE) responses must reach the client incrementally — stream the body and,
|
|
@@ -777,8 +851,11 @@ function makeUwsHandler(port: number | string, isOperationsServer: boolean, requ
|
|
|
777
851
|
};
|
|
778
852
|
}
|
|
779
853
|
logHttpRequest(request, 404, requestId, performance.now() - startTime);
|
|
780
|
-
|
|
854
|
+
const notFoundHeaders = new Headers({ 'content-type': 'text/plain' });
|
|
855
|
+
if (universalHeaders.length > 0) applyUniversalHeaders(notFoundHeaders);
|
|
856
|
+
return { status: 404, headers: notFoundHeaders, body: 'Not found\n' };
|
|
781
857
|
}
|
|
858
|
+
if (universalHeaders.length > 0) applyUniversalHeaders(headers);
|
|
782
859
|
const status = response.status || 200;
|
|
783
860
|
const executionTime = performance.now() - startTime;
|
|
784
861
|
if (!response.handlesHeaders) {
|
|
@@ -942,9 +1019,7 @@ function getBunHTTPServer(port: number, secure: boolean, options: ServerOptions)
|
|
|
942
1019
|
if (!response.headers?.set) {
|
|
943
1020
|
response.headers = new Headers(response.headers);
|
|
944
1021
|
}
|
|
945
|
-
|
|
946
|
-
response.headers.set(key, value);
|
|
947
|
-
}
|
|
1022
|
+
if (universalHeaders.length > 0) applyUniversalHeaders(response.headers);
|
|
948
1023
|
if (response.status === -1) {
|
|
949
1024
|
const fallbackServer = fallbackServers[port];
|
|
950
1025
|
if (fallbackServer) {
|
|
@@ -954,7 +1029,9 @@ function getBunHTTPServer(port: number, secure: boolean, options: ServerOptions)
|
|
|
954
1029
|
return await bunDelegateToNodeServer(fallbackServer, webRequest, request);
|
|
955
1030
|
}
|
|
956
1031
|
logHttpRequest(request, 404, requestId, performance.now() - startTime);
|
|
957
|
-
|
|
1032
|
+
const notFoundHeaders = new globalThis.Headers();
|
|
1033
|
+
if (universalHeaders.length > 0) applyUniversalHeaders(notFoundHeaders);
|
|
1034
|
+
return new Response('Not found\n', { status: 404, headers: notFoundHeaders });
|
|
958
1035
|
}
|
|
959
1036
|
const status = response.status || 200;
|
|
960
1037
|
const endTime = performance.now();
|
|
@@ -990,6 +1067,13 @@ function getBunHTTPServer(port: number, secure: boolean, options: ServerOptions)
|
|
|
990
1067
|
if (body.size) responseHeaders.set('Content-Length', String(body.size));
|
|
991
1068
|
body = body.stream();
|
|
992
1069
|
}
|
|
1070
|
+
} else if (universalHeaders.length > 0) {
|
|
1071
|
+
// handlesHeaders responses write their own headers via the pipe shim below;
|
|
1072
|
+
// pre-set universal headers as defaults — the stream's own setHeader calls
|
|
1073
|
+
// (same name) overwrite these
|
|
1074
|
+
for (const [key, value] of universalHeaders) {
|
|
1075
|
+
responseHeaders.set(key, value);
|
|
1076
|
+
}
|
|
993
1077
|
}
|
|
994
1078
|
// Propagate Connection: close so Bun closes the TCP connection after this response,
|
|
995
1079
|
// preventing stale keep-alive sockets from causing silent hangs on subsequent requests.
|
|
@@ -1067,6 +1151,23 @@ function getBunHTTPServer(port: number, secure: boolean, options: ServerOptions)
|
|
|
1067
1151
|
if (statusCode === 500) harperLogger.warn(errorForLog(error));
|
|
1068
1152
|
else harperLogger.info(errorForLog(error));
|
|
1069
1153
|
} else harperLogger.error(errorForLog(error));
|
|
1154
|
+
const errorHeaders = error.headers;
|
|
1155
|
+
if (errorHeaders || universalHeaders.length > 0) {
|
|
1156
|
+
const headers = new globalThis.Headers();
|
|
1157
|
+
// error.headers may be an iterable Headers or a plain object, same as
|
|
1158
|
+
// toWriteHeadHeaders accepts on the Node path
|
|
1159
|
+
if (errorHeaders?.[Symbol.iterator]) {
|
|
1160
|
+
for (const [key, value] of errorHeaders) headers.append(key, String(value));
|
|
1161
|
+
} else if (errorHeaders) {
|
|
1162
|
+
for (const [key, value] of Object.entries(errorHeaders)) {
|
|
1163
|
+
if (Array.isArray(value)) for (const item of value) headers.append(key, String(item));
|
|
1164
|
+
else headers.set(key, String(value));
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
// universal headers apply to error responses too; error-provided headers win
|
|
1168
|
+
applyUniversalHeaders(headers);
|
|
1169
|
+
return new Response(errorToString(error), { status, headers });
|
|
1170
|
+
}
|
|
1070
1171
|
return new Response(errorToString(error), { status });
|
|
1071
1172
|
}
|
|
1072
1173
|
};
|
|
@@ -1082,6 +1183,9 @@ function getBunHTTPServer(port: number, secure: boolean, options: ServerOptions)
|
|
|
1082
1183
|
};
|
|
1083
1184
|
if (secure) {
|
|
1084
1185
|
// TLS config for Bun
|
|
1186
|
+
// The resolved listener cipher string (getEffectiveTlsCiphers) is deliberately not applied
|
|
1187
|
+
// here: Bun terminates TLS with BoringSSL, which has no OpenSSL @SECLEVEL concept, and the
|
|
1188
|
+
// pseudo-server carries no appliedCiphers stamp so updateTLS's restart warning stays quiet.
|
|
1085
1189
|
const mtls = env.get(serverPrefix + '_mtls');
|
|
1086
1190
|
const tlsSelector = createTLSSelector(usageType ?? 'server', mtls);
|
|
1087
1191
|
// Create a pseudo-server object so the TLS selector can store secureContexts on it
|
|
@@ -1170,6 +1274,7 @@ async function bunDelegateToNodeServer(
|
|
|
1170
1274
|
if (webRequest.headers.get('connection')?.toLowerCase() === 'close') {
|
|
1171
1275
|
webHeaders.set('connection', 'close');
|
|
1172
1276
|
}
|
|
1277
|
+
if (universalHeaders.length > 0) applyUniversalHeaders(webHeaders);
|
|
1173
1278
|
const responseStream = injectResult.stream();
|
|
1174
1279
|
// Event-stream responses (MCP SSE) must reach the client incrementally — return
|
|
1175
1280
|
// the body as a stream. Everything else keeps the prior buffered behavior:
|
|
@@ -1202,7 +1307,9 @@ async function bunDelegateToNodeServer(
|
|
|
1202
1307
|
}
|
|
1203
1308
|
}
|
|
1204
1309
|
// No Fastify instance found — return 404
|
|
1205
|
-
|
|
1310
|
+
const notFoundHeaders = new globalThis.Headers();
|
|
1311
|
+
if (universalHeaders.length > 0) applyUniversalHeaders(notFoundHeaders);
|
|
1312
|
+
return new Response('Not found\n', { status: 404, headers: notFoundHeaders });
|
|
1206
1313
|
}
|
|
1207
1314
|
|
|
1208
1315
|
type SerializedRoute = { host?: string; urlPath?: string; order: string[] };
|
|
@@ -87,9 +87,11 @@ async function operationsServer(options: ServerOptions & { resources?: Resources
|
|
|
87
87
|
// call would mis-tag the secure entry with the plain port and leave the secure
|
|
88
88
|
// listener's chain without authentication.
|
|
89
89
|
if (typeof globalThis.Bun === 'undefined') {
|
|
90
|
-
if (options.port) serverRegistration.http(authentication, { port: options.port });
|
|
91
|
-
if (options.securePort)
|
|
92
|
-
|
|
90
|
+
if (options.port) serverRegistration.http(authentication, { port: options.port, name: 'authentication' });
|
|
91
|
+
if (options.securePort)
|
|
92
|
+
serverRegistration.http(authentication, { securePort: options.securePort, name: 'authentication' });
|
|
93
|
+
if (!options.port && !options.securePort)
|
|
94
|
+
serverRegistration.http(authentication, { port: 'all', name: 'authentication' });
|
|
93
95
|
}
|
|
94
96
|
// On Bun, register the Fastify instance so requests can be delegated via inject()
|
|
95
97
|
if (typeof globalThis.Bun !== 'undefined') {
|
|
@@ -151,7 +151,11 @@ export function createSSEResponseStream(emitter: ProgressEmitter, operation: ()
|
|
|
151
151
|
* operand so every line is written regardless of the accumulated backpressure flag.
|
|
152
152
|
*/
|
|
153
153
|
function writeSSE(stream: PassThrough, event: ProgressEvent): boolean {
|
|
154
|
-
|
|
154
|
+
// `JSON.stringify(undefined)` returns the `undefined` primitive, not a string, so fall back to
|
|
155
|
+
// '' via `??` — an event carrying no data still writes a valid (empty-payload) record instead
|
|
156
|
+
// of throwing a TypeError deep in the write path. An explicit `null` is preserved as the JSON
|
|
157
|
+
// value `null` for consumers replaying persisted events.
|
|
158
|
+
const data = typeof event.data === 'string' ? event.data : (JSON.stringify(event.data) ?? '');
|
|
155
159
|
let canWrite = stream.write(`event: ${event.event}\n`);
|
|
156
160
|
for (const line of data.split(/\r?\n/)) {
|
|
157
161
|
canWrite = stream.write(`data: ${line}\n`) && canWrite;
|
|
@@ -27,7 +27,7 @@ const {
|
|
|
27
27
|
} = require('../../components/shutdownDrain.ts');
|
|
28
28
|
const { realExit } = require('./workerProcessGuard.ts');
|
|
29
29
|
const { isBun } = require('../serverHelpers/Request.ts');
|
|
30
|
-
const { createTLSSelector } = require('../../security/keys.ts');
|
|
30
|
+
const { createTLSSelector, getEffectiveTlsCiphers } = require('../../security/keys.ts');
|
|
31
31
|
const { startupLog } = require('../../bin/run.ts');
|
|
32
32
|
const { SERVERS, setPortServerMap, portServer } = require('../serverRegistry.ts');
|
|
33
33
|
const httpComponent = require('../http.ts');
|
|
@@ -570,7 +570,11 @@ function onSocket(listener, options) {
|
|
|
570
570
|
if (options.securePort) {
|
|
571
571
|
setPortServerMap(options.securePort, { protocol_name: 'TLS', name: getComponentName() });
|
|
572
572
|
const SNICallback = createTLSSelector('server', options.mtls);
|
|
573
|
-
|
|
573
|
+
// OpenSSL takes the cipher list (and its @SECLEVEL) from the context the server was created with;
|
|
574
|
+
// a context swapped in by the SNI callback doesn't carry its own cipher list onto the connection.
|
|
575
|
+
// The listener-level string is therefore the only one honored — resolve it from every configured
|
|
576
|
+
// source (see resolveEffectiveTlsCiphers in keys.ts).
|
|
577
|
+
const effectiveCiphers = getEffectiveTlsCiphers('server', options.mtls);
|
|
574
578
|
socketServer = createSecureSocketServer(
|
|
575
579
|
{
|
|
576
580
|
rejectUnauthorized: Boolean(options.mtls?.required),
|
|
@@ -578,13 +582,13 @@ function onSocket(listener, options) {
|
|
|
578
582
|
noDelay: true, // don't delay for Nagle's algorithm, it is a relic of the past that slows things down: https://brooker.co.za/blog/2024/05/09/nagle.html
|
|
579
583
|
keepAlive: true,
|
|
580
584
|
keepAliveInitialDelay: 600, // 10 minute keep-alive, want to be proactive about closing unused connections
|
|
581
|
-
|
|
582
|
-
// count of how many node TLS bugs that makes
|
|
583
|
-
ciphers: tlsConfig.ciphers ?? tlsConfig[0]?.ciphers,
|
|
585
|
+
ciphers: effectiveCiphers,
|
|
584
586
|
SNICallback,
|
|
585
587
|
},
|
|
586
588
|
listener
|
|
587
589
|
);
|
|
590
|
+
socketServer.appliedCiphers = effectiveCiphers ?? null;
|
|
591
|
+
socketServer.verifiesClientCerts = Boolean(options.mtls);
|
|
588
592
|
SNICallback.initialize(socketServer);
|
|
589
593
|
// Only opt out of reusePort on macOS, which doesn't reliably support SO_REUSEPORT on all
|
|
590
594
|
// socket types (ENOTSUP). Everywhere else, sharing the port lets every worker accept
|
package/sqlTranslator/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ import * as hdbUtils from '../utility/common_utils.ts';
|
|
|
16
16
|
import * as terms from '../utility/hdbTerms.ts';
|
|
17
17
|
import { handleHDBError } from '../utility/errors/hdbError.ts';
|
|
18
18
|
import { HTTP_STATUS_CODES } from '../utility/errors/commonErrors.ts';
|
|
19
|
+
import * as sqlEngineRouter from '../sqlEngine/router.ts';
|
|
19
20
|
|
|
20
21
|
//here we call to define and import custom functions to alasql
|
|
21
22
|
alasqlFunctionImporter(alasql);
|
|
@@ -158,13 +159,22 @@ export function processAST(jsonMessage: any, parsedSqlObject: any, callback: any
|
|
|
158
159
|
default:
|
|
159
160
|
throw new Error(`unsupported SQL type ${parsedSqlObject.variant} in SQL: ${jsonMessage}`);
|
|
160
161
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
|
|
163
|
+
sqlEngineRouter.route(
|
|
164
|
+
{
|
|
165
|
+
variant: parsedSqlObject.variant,
|
|
166
|
+
jsonMessage,
|
|
167
|
+
statement,
|
|
168
|
+
legacy: sqlFunction,
|
|
169
|
+
},
|
|
170
|
+
(err, data) => {
|
|
171
|
+
if (err) {
|
|
172
|
+
callback(err);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
callback(null, data);
|
|
165
176
|
}
|
|
166
|
-
|
|
167
|
-
});
|
|
177
|
+
);
|
|
168
178
|
} catch (e) {
|
|
169
179
|
return callback(e);
|
|
170
180
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as e,t}from"./rolldown-runtime-CNC7AqOf.js";import{g as n,t as r}from"./button-i10JPNcp.js";import{C as i,S as a,_ as o,a as s,b as c,c as l,d as u,g as d,i as f,l as p,m,p as h,r as g,s as _,t as v,u as ee,v as y,w as b,x,y as S}from"./vendor-core-vIXn_E4D.js";import{C as te,I as ne,N as re,j as ie}from"./vendor-tanstack-Dl75hT33.js";import{a as ae}from"./vendor-datadog-DBn-aOxh.js";import{r as oe}from"./vendor-react-B36dp27u.js";import{zt as C}from"./vendor-ui-Bm4W8wqw.js";import{t as w}from"./createLucideIcon-B49W6uF4.js";import{l as se,t as ce}from"./react-SMksfEpE.js";import{a as le,i as ue,n as de,r as fe,t as pe}from"./x-Bru-nCti.js";import{_ as me,d as he,g as ge,h as _e,i as ve,l as ye,m as be,p as xe,r as Se,t as Ce,u as we}from"./setComponentFile-
|
|
1
|
+
import{a as e,t}from"./rolldown-runtime-CNC7AqOf.js";import{g as n,t as r}from"./button-i10JPNcp.js";import{C as i,S as a,_ as o,a as s,b as c,c as l,d as u,g as d,i as f,l as p,m,p as h,r as g,s as _,t as v,u as ee,v as y,w as b,x,y as S}from"./vendor-core-vIXn_E4D.js";import{C as te,I as ne,N as re,j as ie}from"./vendor-tanstack-Dl75hT33.js";import{a as ae}from"./vendor-datadog-DBn-aOxh.js";import{r as oe}from"./vendor-react-B36dp27u.js";import{zt as C}from"./vendor-ui-Bm4W8wqw.js";import{t as w}from"./createLucideIcon-B49W6uF4.js";import{l as se,t as ce}from"./react-SMksfEpE.js";import{a as le,i as ue,n as de,r as fe,t as pe}from"./x-Bru-nCti.js";import{_ as me,d as he,g as ge,h as _e,i as ve,l as ye,m as be,p as xe,r as Se,t as Ce,u as we}from"./setComponentFile-BVDWRYxx.js";import{n as Te}from"./queryClient-DK1L0t7F.js";import{n as Ee,t as De}from"./localStorageKeys-D_mgWLDC.js";import{$n as Oe,At as ke,Dt as Ae,Et as je,G as Me,Kn as Ne,Ot as Pe,Pn as Fe,R as Ie,Rt as Le,Tt as Re,Un as ze,Vn as Be,Wn as Ve,Yn as He,ar as Ue,c as We,d as Ge,ir as Ke,l as qe,lr as Je,mt as Ye,o as Xe,qt as Ze,rr as Qe,rt as $e,st as et,z as tt}from"./index-BdbBanDP.js";import{t as nt}from"./useEntityRestURL-CU_lY6XW.js";import{n as rt}from"./getAnalytics-CmXDoq4g.js";var it=w(`between-horizontal-start`,[[`rect`,{width:`13`,height:`7`,x:`8`,y:`3`,rx:`1`,key:`pkso9a`}],[`path`,{d:`m2 9 3 3-3 3`,key:`1agib5`}],[`rect`,{width:`13`,height:`7`,x:`8`,y:`14`,rx:`1`,key:`1q5fc1`}]]),at=w(`book`,[[`path`,{d:`M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20`,key:`k3hazp`}]]),ot=w(`chart-area`,[[`path`,{d:`M3 3v16a2 2 0 0 0 2 2h16`,key:`c24i48`}],[`path`,{d:`M7 11.207a.5.5 0 0 1 .146-.353l2-2a.5.5 0 0 1 .708 0l3.292 3.292a.5.5 0 0 0 .708 0l4.292-4.292a.5.5 0 0 1 .854.353V16a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1z`,key:`q0gr47`}]]),st=w(`circle-x`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m15 9-6 6`,key:`1uzhvr`}],[`path`,{d:`m9 9 6 6`,key:`z0biqf`}]]),ct=w(`file-pen`,[[`path`,{d:`M12.659 22H18a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v9.34`,key:`o6klzx`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`M10.378 12.622a1 1 0 0 1 3 3.003L8.36 20.637a2 2 0 0 1-.854.506l-2.867.837a.5.5 0 0 1-.62-.62l.836-2.869a2 2 0 0 1 .506-.853z`,key:`zhnas1`}]]),lt=w(`logs`,[[`path`,{d:`M3 5h1`,key:`1mv5vm`}],[`path`,{d:`M3 12h1`,key:`lp3yf2`}],[`path`,{d:`M3 19h1`,key:`w6f3n9`}],[`path`,{d:`M8 5h1`,key:`1nxr5w`}],[`path`,{d:`M8 12h1`,key:`1con00`}],[`path`,{d:`M8 19h1`,key:`k7p10e`}],[`path`,{d:`M13 5h8`,key:`a7qcls`}],[`path`,{d:`M13 12h8`,key:`h98zly`}],[`path`,{d:`M13 19h8`,key:`c3s6r1`}]]),ut=w(`message-square-heart`,[[`path`,{d:`M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z`,key:`18887p`}],[`path`,{d:`M7.5 9.5c0 .687.265 1.383.697 1.844l3.009 3.264a1.14 1.14 0 0 0 .407.314 1 1 0 0 0 .783-.004 1.14 1.14 0 0 0 .398-.31l3.008-3.264A2.77 2.77 0 0 0 16.5 9.5 2.5 2.5 0 0 0 12 8a2.5 2.5 0 0 0-4.5 1.5`,key:`1faxuh`}]]),dt=w(`send`,[[`path`,{d:`M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z`,key:`1ffxy3`}],[`path`,{d:`m21.854 2.147-10.94 10.939`,key:`12cjpa`}]]),ft=w(`wrench`,[[`path`,{d:`M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z`,key:`1ngwbx`}]]);async function pt(){await n.delete(`/Chat/Messages/`)}var T=e(ae(),1),E=oe();function mt({setMessages:e}){let[t,n]=(0,T.useState)(!1);return(0,E.jsxs)(`button`,{type:`button`,className:`clear-chat-button gap-1`,onClick:(0,T.useCallback)(async()=>{if(!t){n(!0);try{await pt(),e([])}catch(e){console.error(`Failed to clear chat:`,e)}finally{n(!1)}}},[t,e]),disabled:t,title:`Clear chat`,children:[t?(0,E.jsx)(de,{className:`animate-spin`,size:18}):(0,E.jsx)(Ve,{size:18}),`Clear`]})}async function ht(){let{data:e}=await n.get(`/Chat/Messages/`);return e}var gt=`vercel.ai.error`,_t=Symbol.for(gt),vt,yt,D=class e extends (yt=Error,vt=_t,yt){constructor({name:e,message:t,cause:n}){super(t),this[vt]=!0,this.name=e,this.cause=n}static isInstance(t){return e.hasMarker(t,gt)}static hasMarker(e,t){let n=Symbol.for(t);return typeof e==`object`&&!!e&&n in e&&typeof e[n]==`boolean`&&e[n]===!0}};function bt(e){return e==null?`unknown error`:typeof e==`string`?e:e instanceof Error?e.toString():JSON.stringify(e)}var xt=`AI_InvalidArgumentError`,St=`vercel.ai.error.${xt}`,Ct=Symbol.for(St),wt,Tt,Et=class extends (Tt=D,wt=Ct,Tt){constructor({message:e,cause:t,argument:n}){super({name:xt,message:e,cause:t}),this[wt]=!0,this.argument=n}static isInstance(e){return D.hasMarker(e,St)}},Dt=`AI_JSONParseError`,Ot=`vercel.ai.error.${Dt}`,kt=Symbol.for(Ot),At,jt,Mt=class extends (jt=D,At=kt,jt){constructor({text:e,cause:t}){super({name:Dt,message:`JSON parsing failed: Text: ${e}.
|
|
2
2
|
Error message: ${bt(t)}`,cause:t}),this[At]=!0,this.text=e}static isInstance(e){return D.hasMarker(e,Ot)}},Nt=`AI_TypeValidationError`,Pt=`vercel.ai.error.${Nt}`,Ft=Symbol.for(Pt),It,Lt,O=class e extends (Lt=D,It=Ft,Lt){constructor({value:e,cause:t,context:n}){let r=`Type validation failed`;if(n?.field&&(r+=` for ${n.field}`),n?.entityName||n?.entityId){r+=` (`;let e=[];n.entityName&&e.push(n.entityName),n.entityId&&e.push(`id: "${n.entityId}"`),r+=e.join(`, `),r+=`)`}super({name:Nt,message:`${r}: Value: ${JSON.stringify(e)}.
|
|
3
3
|
Error message: ${bt(t)}`,cause:t}),this[It]=!0,this.value=e,this.context=n}static isInstance(e){return D.hasMarker(e,Pt)}static wrap({value:t,cause:n,context:r}){return e.isInstance(n)&&n.value===t&&n.context?.field===r?.field&&n.context?.entityName===r?.entityName&&n.context?.entityId===r?.entityId?n:new e({value:t,cause:n,context:r})}},Rt=class extends Error{constructor(e,t){super(e),this.name=`ParseError`,this.type=t.type,this.field=t.field,this.value=t.value,this.line=t.line}},zt=10,Bt=13,k=32;function Vt(e){}function Ht(e){if(typeof e==`function`)throw TypeError("`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?");let{onEvent:t=Vt,onError:n=Vt,onRetry:r=Vt,onComment:i,maxBufferSize:a}=e,o=[],s=0,c=!0,l,u=``,d=0,f,p=!1;function m(e){if(p)throw Error("Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing.");if(c&&(c=!1,e.charCodeAt(0)===239&&e.charCodeAt(1)===187&&e.charCodeAt(2)===191&&(e=e.slice(3))),o.length===0){let t=g(e);t!==``&&(o.push(t),s=t.length),h();return}if(e.indexOf(`
|
|
4
4
|
`)===-1&&e.indexOf(`\r`)===-1){o.push(e),s+=e.length,h();return}o.push(e);let t=o.join(``);o.length=0,s=0;let n=g(t);n!==``&&(o.push(n),s=n.length),h()}function h(){a!==void 0&&(s+u.length<=a||(p=!0,o.length=0,s=0,l=void 0,u=``,d=0,f=void 0,n(new Rt(`Buffered data exceeded max buffer size of ${a} characters`,{type:`max-buffer-size-exceeded`}))))}function g(e){let n=0;if(e.indexOf(`\r`)===-1){let r=e.indexOf(`
|
|
@@ -1946,4 +1946,4 @@ jsResource:
|
|
|
1946
1946
|
`,"using-blob-datatype":"---\nname: using-blob-datatype\ndescription: How to use the Blob data type for efficient binary storage in Harper.\nmetadata:\n mode: generate\n sources:\n - reference/v5/database/schema.md#Blob Type\n - reference/v5/database/api.md#Streaming\n - reference/v5/database/api.md#`BlobOptions`\n - reference/v5/database/api.md#Blob Coercion\n sourceCommit: f37a8c4021e20d5c74c1d339a6b6c8c196b5603e\n inputHash: 92e03eb0b830f335\n---\n\n# Using the Blob Data Type\n\nInstructions for the agent to follow when storing and retrieving large binary content using the `Blob` data type in Harper.\n\n## When to Use\n\nApply this rule when a schema field needs to store large binary content such as images, video, audio, or large HTML — typically content larger than 20KB. Use `Blob` instead of `Bytes` when streaming support and out-of-record storage are required. See [handling-binary-data.md](handling-binary-data.md) for broader binary data guidance.\n\n## How It Works\n\n1. **Declare a `Blob` field in your schema**: Add a field typed as `Blob` to your `@table` type.\n\n ```graphql\n type MyTable @table {\n id: Any! @primaryKey\n data: Blob\n }\n ```\n\n2. **Create and store a blob with `createBlob()`**: Pass a buffer or stream to `createBlob()`, then `put` the record.\n\n ```javascript\n let blob = createBlob(largeBuffer);\n await MyTable.put({ id: 'my-record', data: blob });\n ```\n\n3. **Retrieve blob data using standard Web API methods**: The `Blob` type implements the Web API `Blob` interface. Use `.bytes()`, `.text()`, `.arrayBuffer()`, `.stream()`, or `.slice()` as needed.\n\n ```javascript\n let record = await MyTable.get('my-record');\n let buffer = await record.data.bytes(); // ArrayBuffer\n let text = await record.data.text(); // string\n let stream = record.data.stream(); // ReadableStream\n ```\n\n4. **Use `saveBeforeCommit` when full write must precede commit**: By default, `Blob` is not ACID-compliant — a record can reference a blob before it is fully written. Set `saveBeforeCommit: true` to block the transaction until the blob is fully saved.\n\n ```javascript\n let blob = createBlob(stream, { saveBeforeCommit: true });\n await MyTable.put({ id: 'my-record', data: blob });\n // put() resolves only after blob is fully written and record is committed\n ```\n\n5. **Register an error handler when returning a blob via REST**: Interrupted streams must be handled explicitly.\n\n ```javascript\n export class MyEndpoint extends MyTable {\n static async get(target) {\n const record = super.get(target);\n let blob = record.data;\n blob.on('error', () => {\n MyTable.invalidate(target);\n });\n return { status: 200, headers: {}, body: blob };\n }\n }\n ```\n\n6. **Rely on automatic coercion where applicable**: When a field is typed as `Blob` in the schema, any string or buffer assigned via `put`, `patch`, or `publish` is automatically coerced to a `Blob` — no manual `createBlob()` call is needed in those cases.\n\n### `BlobOptions` reference\n\nPass an options object as the second argument to `createBlob()`.\n\n| Option | Type | Default | Description |\n| ------------------ | --------- | ----------- | ------------------------------------------------------------------------------------------------------------------------ |\n| `type` | `string` | `undefined` | MIME type to associate with the blob (e.g., `image/jpeg`). Readable via `blob.type` and used when serving HTTP. |\n| `size` | `number` | `undefined` | Size of the data in bytes, if known ahead of time. Otherwise inferred from a buffer or determined as a stream completes. |\n| `saveBeforeCommit` | `boolean` | `false` | Wait until the blob is fully written before the transaction commits. |\n| `compress` | `boolean` | `false` | Compress the stored data with deflate. |\n| `flush` | `boolean` | `false` | Flush the file to disk after writing, before the `createBlob` promise chain resolves. |\n\n## Examples\n\n**Store an image with a MIME type:**\n\n```javascript\nlet blob = createBlob(imageBuffer, { type: 'image/jpeg' });\nawait Photo.put({ id, data: blob });\n```\n\n**Stream a blob in as it streams out (low-latency passthrough):**\n\n```javascript\nlet blob = createBlob(incomingStream);\n// blob exists, but data is still streaming to storage\nawait MyTable.put({ id: 'my-record', data: blob });\n\nlet record = await MyTable.get('my-record');\n// blob data is accessible as it arrives\nlet outgoingStream = record.data.stream();\n```\n\n**Guarantee full write before commit using `saveBeforeCommit`:**\n\n```javascript\nlet blob = createBlob(stream, { saveBeforeCommit: true });\nawait MyTable.put({ id: 'my-record', data: blob });\n```\n\n## Notes\n\n- `Blob` stores data separately from the record. If you need the binary data to be a true, ACID-committed part of the record, use a `Bytes` field instead.\n- All standard Web API `Blob` methods — `.text()`, `.arrayBuffer()`, `.stream()`, `.slice()`, and `.bytes()` — are available on retrieved blob fields.\n- Without `saveBeforeCommit: true`, blobs are **not** ACID-compliant by default; a record can reference a blob before it is fully written to storage.\n","vector-indexing":'---\nname: vector-indexing\ndescription: How to enable and query vector indexes for similarity search in Harper.\nmetadata:\n mode: generate\n sources:\n - reference/v5/database/schema.md#Vector Indexing\n sourceCommit: 4fe4c9c95e0974eaa77032f6f10e36fbd8ec64ac\n inputHash: d90b1b74597d08a6\n---\n\n# Vector Indexing\n\nInstructions for the agent to enable HNSW vector indexes on table fields and query them for similarity search in Harper.\n\n## When to Use\n\nApply this rule when adding a vector similarity search capability to a Harper table — for example, storing text embeddings and querying for nearest neighbors, filtering by distance threshold, or tuning index construction and search parameters. Use it alongside [adding-tables-with-schemas.md](adding-tables-with-schemas.md) when defining the schema that hosts the vector field.\n\n## How It Works\n\n1. **Declare the vector index on a field**: Add `@indexed(type: "HNSW")` to a `[Float]` field inside a `@table` type. This creates an HNSW (Hierarchical Navigable Small World) index for approximate nearest-neighbor search.\n\n ```graphql\n type Document @table {\n id: Long @primaryKey\n textEmbeddings: [Float] @indexed(type: "HNSW")\n }\n ```\n\n2. **Query by nearest neighbors using `sort`**: Call `.search()` with a `sort` descriptor that specifies the indexed `attribute` and a `target` vector. Use `limit` to cap results.\n\n ```javascript\n let results = Document.search({\n sort: { attribute: \'textEmbeddings\', target: searchVector },\n limit: 5,\n });\n ```\n\n3. **Combine with filter conditions**: Add a `conditions` array alongside `sort` to pre-filter records before ranking by similarity.\n\n ```javascript\n let results = Document.search({\n conditions: [{ attribute: \'price\', comparator: \'lt\', value: 50 }],\n sort: { attribute: \'textEmbeddings\', target: searchVector },\n limit: 5,\n });\n ```\n\n4. **Filter by distance threshold**: To return only records within a similarity cutoff (without ranking), place `target` directly on the condition alongside `comparator` and `value`. This bounds result quality rather than ranking by similarity.\n\n ```javascript\n let results = Document.search({\n conditions: {\n attribute: \'textEmbeddings\',\n comparator: \'lt\',\n value: 0.1,\n target: searchVector,\n },\n });\n ```\n\n5. **Include computed distance in results**: Use the special `$distance` field in `select` to return the distance from the target vector. Available in both `sort`-based and threshold-based queries.\n\n ```javascript\n let results = Document.search({\n select: [\'name\', \'$distance\'],\n sort: { attribute: \'textEmbeddings\', target: searchVector },\n limit: 5,\n });\n ```\n\n6. **Tune per-query search options**: Pass `distance` and `ef` directly on the `sort` descriptor to override index defaults for a single query.\n\n ```javascript\n let results = Document.search({\n sort: { attribute: \'textEmbeddings\', target: searchVector, distance: \'dotProduct\', ef: 200 },\n limit: 5,\n });\n ```\n\n - `distance` — overrides the distance function for this query: `"cosine"`, `"euclidean"`, or `"dotProduct"`.\n - `ef` — overrides the search exploration budget. Higher values improve recall at the cost of latency.\n\n7. **Configure HNSW index parameters**: Pass parameters directly in the `@indexed` directive. Structural parameters (`distance`, `M`, `efConstruction`, `quantization`) trigger an index rebuild when changed; `efConstructionSearch` does not.\n\n ```graphql\n type Document @table {\n id: Long @primaryKey\n textEmbeddings: [Float]\n @indexed(type: "HNSW", distance: "euclidean", optimizeRouting: 0, efConstructionSearch: 100)\n }\n ```\n\n8. **Enable vector quantization**: Use `quantization: "int8"` to store vectors as 8-bit integers, reducing index size and memory usage. Harper re-ranks nearest-neighbor `sort` results against full-precision vectors automatically.\n\n ```graphql\n type Document @table {\n id: Long @primaryKey\n textEmbeddings: [Float] @indexed(type: "HNSW", quantization: "int8")\n }\n ```\n\n## Examples\n\nFull schema with custom HNSW parameters and a nearest-neighbor query with distance output:\n\n```graphql\ntype Document @table {\n id: Long @primaryKey\n textEmbeddings: [Float]\n @indexed(type: "HNSW", distance: "euclidean", optimizeRouting: 0, efConstructionSearch: 100)\n}\n```\n\n```javascript\n// Nearest-neighbor search with distance scores\nlet results = Document.search({\n select: [\'name\', \'$distance\'],\n sort: { attribute: \'textEmbeddings\', target: searchVector },\n limit: 5,\n});\n\n// Distance-threshold query (no ranking)\nlet closeMatches = Document.search({\n conditions: {\n attribute: \'textEmbeddings\',\n comparator: \'lt\',\n value: 0.1,\n target: searchVector,\n },\n});\n```\n\n## Notes\n\n### HNSW Parameters\n\n| Parameter | Default | Description |\n| ---------------------- | ----------------- | ------------------------------------------------------------------------------------------------------ |\n| `distance` | `"cosine"` | Distance function: `"cosine"`, `"euclidean"`, or `"dotProduct"` |\n| `efConstruction` | `100` | Max nodes explored during index construction. Higher = better recall, lower = better performance |\n| `M` | `16` | Preferred connections per graph layer. Higher = more space, better recall for high-dimensional data |\n| `optimizeRouting` | `0.5` | Heuristic aggressiveness for omitting redundant connections (0 = off, 1 = most aggressive) |\n| `mL` | computed from `M` | Normalization factor for level generation |\n| `efConstructionSearch` | auto-scaled | Max nodes explored during search. When unset, auto-scales with index size; setting it fixes the budget |\n| `quantization` | — | `"int8"` stores vectors quantized to int8 |\n\n- The `distance` option on a per-query `sort` descriptor accepts `"cosine"`, `"euclidean"`, or `"dotProduct"`.\n- When no `ef` is passed and `efConstructionSearch` (or `efConstruction`) is not explicitly set on the index, the search budget auto-scales with index size.\n- `efConstruction` seeds the initial value of `efConstructionSearch`; setting either one fixes the search budget.\n- The correct parameter name is `efConstructionSearch` (not `efSearchConstruction`).\n- `$distance` is available in both `sort`-based ranking and `conditions`-based threshold queries.\n- For `quantization: "int8"`, distance-threshold (`lt`/`le`) queries filter on approximate distance; `sort` queries re-rank against full-precision vectors.\n'},oa={name:`readHarperSkill`,description:`Returns documentation for a Harper skill or best practice. Skills provide guidance on developing Harper applications.`,inputSchema:o({skill:g(ia)})};async function sa({input:{skill:e}}){return{success:!!aa[e],message:aa[e]||`No skill found with the name ${e}`}}var ca={...oa,icon:at,execute:sa},la={name:`readLogs`,description:`Returns the matching logs from the server.`,inputSchema:o({log_name:g([`hdb.log`,`system.log`]).default(`hdb.log`),limit:c().or(s()).optional(),level:g([`notify`,`error`,`warn`,`info`,`debug`,`trace`,`undefined`]).or(s()).optional(),from:c().or(s()).optional(),until:c().or(s()).optional()})};async function ua({input:e,instanceClientParams:t}){try{return{success:!0,data:await Xe({...t,logFilters:e,replicated:t.entityType===`cluster`})}}catch(e){return{success:!1,message:`Error: ${e}`}}}var da={...la,icon:lt,execute:ua},fa={name:`readTableRecords`,description:`Retrieves some or all table records from a database on the server.`,inputSchema:o({database:c().trim(),table:c().trim(),pageIndex:d().default(0),pageSize:d().default(10),primaryKey:c(),conditions:l(o({search_attribute:c(),search_type:g([`between`,`eq`,`equals`,`greater_than`,`greater_than_equal`,`less_than`,`less_than_equal`,`ne`,`not_equal`,`starts_with`]),search_value:_()})),sort:o({attribute:c(),descending:p()})})};async function pa({input:{database:e,table:t,conditions:n,primaryKey:r,...i},instanceClientParams:a}){try{if(!n.length){let{data:n}=await je({...a,databaseName:e,tableName:t,onlyIfCached:!0,searchAttribute:r,...i});return{success:!0,data:n}}let{data:o}=await tt({...a,databaseName:e,tableName:t,onlyIfCached:!0,conditions:n,...i});return{success:!0,data:o}}catch(e){return{success:!1,message:`Error: ${e}`}}}var ma={...fa,icon:He,execute:pa},ha={name:`restartHTTPService`,description:`Restarts the HTTP service on the server to allow schema and resource changes to be applied.`,inputSchema:o({})};async function ga({instanceClientParams:e,baseURL:t}){let n=C.loading(`Restarting HTTP service...`,{description:`This may take a bit.`,duration:3e5});try{await Ye({...e,operation:`restart_service`,replicated:e.entityType===`cluster`})}catch(e){return{success:!1,message:`Error: ${e}`}}return C.success(`Done!`,{description:`HTTP Service restarted!`,id:n,duration:5e3}),{success:!0,message:`HTTP Service restarted!`,webURL:t}}var _a={...ha,icon:de,execute:ga,requiresApproval:!0},va={name:`setComponentFile`,description:`Returns the contents of a component file by its full path (which was returned by getComponents)`,inputSchema:o({path:c().trim(),payload:c(),encoding:g([`utf8`,`ASCII`,`binary`,`hex`,`base64`,`utf16le`,`latin1`,`ucs2`])})};async function ya({input:{path:e,encoding:t,payload:n},instanceClientParams:r}){try{let i=e.split(`/`),a=i.shift(),o=i.join(`/`),s=await Ce({...r,file:o,project:a,payload:n,encoding:t});return await Te.invalidateQueries({queryKey:[r.entityId,`get_component_file`,a,o]}),ke(`ReloadApplicationRootEntries`,!0),{success:!0,data:s}}catch(e){return{success:!1,message:`Error: ${e}`}}}var ba={...va,icon:ct,execute:ya,requiresApproval:!0},xa={name:`updateTableRecords`,description:`Updates records in a particular table in a particular database on the server.`,inputSchema:o({database:c().trim(),table:c().trim(),records:l(_())})};async function Sa({input:{database:e,table:t,records:n},instanceClientParams:r,params:i}){try{let a=await We({...r,databaseName:e,tableName:t,records:n}),{databaseName:o,tableName:s}=i;return await Te.invalidateQueries({queryKey:[r.entityId,o,s]}),{success:!0,data:a}}catch(e){return{success:!1,message:`Error: ${e}`}}}var Ca={readHarperSkill:ca,createApp:Ei,readLogs:da,getAnalytics:Fi,listAnalyticsMetrics:ra,restartHTTPService:_a,collectFeedback:Ci,getUserContext:Xi,getComponentFile:Ri,getComponents:Vi,setComponentFile:ba,dropComponentFile:Mi,getDescribeAll:Wi,getDescribeTable:qi,insertTableRecords:$i,readTableRecords:ma,updateTableRecords:{...xa,icon:it,execute:Sa,requiresApproval:!0},deleteTableRecords:ki};function wa(e){return Ca[e]}function Ta(e){return e.state===`input-available`&&!!wa(Zr(e))?.requiresApproval}function Ea(e){let t=[];for(let[n,r]of(e??[]).entries()){if(G(r)){if(Ta(r)){t.push({kind:`part`,part:r,index:n});continue}let e=t.at(-1);e?.kind===`tool-group`?e.parts.push(r):t.push({kind:`tool-group`,parts:[r],index:n});continue}qr(r)&&r.text.length>0&&t.push({kind:`part`,part:r,index:n})}return t}function Da({part:e,onApprove:t,onDeny:n,onAlwaysApprove:i,isApproving:a}){let[o,s]=(0,T.useState)(!1),[c,l]=(0,T.useState)(!1),u=Zr(e),d=wa(u),f=d?.icon||Ke,p=d?.requiresApproval,m=(0,T.useMemo)(()=>!e.input||typeof e.input==`object`&&Object.keys(e.input).length===0,[e.input]),h=(0,T.useMemo)(()=>{let t=JSON.stringify(e.input,null,` `);return{json:t,lines:t?t.split(`
|
|
1947
1947
|
`).length:0}},[e.input]),g=(0,T.useMemo)(()=>{let t=JSON.stringify(e.output,null,` `);return{json:t,lines:t?t.split(`
|
|
1948
1948
|
`).length:0}},[e.output]);return(0,E.jsxs)(`div`,{className:`tool-invocation ${e.state}`,children:[(0,E.jsxs)(`div`,{className:`tool-info`,children:[(0,E.jsxs)(`div`,{className:`tool-name`,children:[(0,E.jsx)(f,{size:14}),(0,E.jsx)(`span`,{children:u})]}),(0,E.jsxs)(`div`,{className:`tool-status`,children:[e.state===`input-streaming`&&(0,E.jsx)(`span`,{children:`Thinking...`}),e.state===`input-available`&&(0,E.jsx)(`span`,{children:a?`Executing...`:p?`Awaiting Approval...`:`Executing...`}),e.state===`output-available`&&(e.output?.error?(0,E.jsx)(st,{size:14,className:`text-destructive`}):(0,E.jsx)(le,{size:14}))]})]}),e.state!==`input-streaming`&&(0,E.jsxs)(`div`,{className:`tool-io`,children:[!m&&(0,E.jsxs)(`div`,{className:`tool-args`,children:[(0,E.jsxs)(`div`,{className:`flex items-center justify-between gap-2 mb-1`,children:[(0,E.jsx)(`strong`,{children:`Input:`}),h.lines>3&&(0,E.jsx)(r,{type:`button`,variant:`ghost`,size:`sm`,className:`h-6 px-2 text-[10px] uppercase tracking-wider text-muted-foreground hover:text-foreground`,onClick:()=>s(!o),children:o?(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(fe,{size:12}),`Hide`]}):(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(ue,{size:12}),`Show`]})})]}),(0,E.jsx)(`div`,{className:o?`whitespace-pre-wrap`:`line-clamp-3 overflow-hidden whitespace-pre-wrap`,children:h.json})]}),e.state===`input-available`&&p&&(0,E.jsxs)(`div`,{className:`flex gap-2 mt-3 pt-3 border-t`,children:[(0,E.jsxs)(r,{size:`sm`,className:`h-8 text-xs bg-green-600 hover:bg-green-700 text-white`,onClick:()=>t?.(e.toolCallId),disabled:a,children:[a?(0,E.jsx)(Qe,{className:`mr-2 h-3 w-3 animate-spin`}):null,`Approve`]}),(0,E.jsx)(r,{type:`button`,size:`sm`,variant:`outline`,className:`h-8 text-xs approval-outline`,onClick:()=>i?.(e.toolCallId),disabled:a,children:`Always Approve`}),(0,E.jsx)(r,{type:`button`,size:`sm`,variant:`outline`,className:`h-8 text-xs approval-outline`,onClick:()=>n?.(e.toolCallId),disabled:a,children:`Deny`})]}),e.state===`output-available`&&(0,E.jsx)(E.Fragment,{children:d?.render?d.render(e):(0,E.jsxs)(`div`,{className:`tool-result`,children:[(0,E.jsxs)(`div`,{className:`flex items-center justify-between gap-2 mb-1`,children:[(0,E.jsx)(`strong`,{children:`Result:`}),g.lines>3&&(0,E.jsx)(r,{type:`button`,variant:`ghost`,size:`sm`,className:`h-6 px-2 text-[10px] uppercase tracking-wider text-muted-foreground hover:text-foreground`,onClick:()=>l(!c),children:c?(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(fe,{size:12}),`Hide`]}):(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(ue,{size:12}),`Show`]})})]}),(0,E.jsx)(`div`,{className:c?`whitespace-pre-wrap`:`line-clamp-3 overflow-hidden whitespace-pre-wrap`,children:g.json})]})})]})]})}function Oa({parts:e,onApprove:t,onDeny:n,onAlwaysApprove:r,approvingToolCallIds:i}){let[a,o]=(0,T.useState)(!1),s=e.some(e=>e.state!==`output-available`&&e.state!==`output-error`),c=e.some(e=>e.state===`output-error`||e.state===`output-available`&&e.output?.error),l=e.length===1?Zr(e[0]):void 0,u=l&&wa(l)?.icon||ft,d=l??`${e.length} tools`;return(0,E.jsxs)(`div`,{className:`tool-group`,children:[(0,E.jsxs)(`button`,{type:`button`,className:`tool-group-summary`,"aria-expanded":a,onClick:()=>o(!a),children:[a?(0,E.jsx)(ue,{size:14}):(0,E.jsx)(Je,{size:14}),(0,E.jsx)(u,{size:14}),(0,E.jsx)(`span`,{children:s?`Using ${d}...`:`Used ${d}`}),(0,E.jsx)(`span`,{className:`tool-group-status`,children:s?(0,E.jsx)(Qe,{size:14,className:`animate-spin`}):c?(0,E.jsx)(st,{size:14,className:`text-destructive`}):(0,E.jsx)(le,{size:14})})]}),a&&e.map(e=>(0,E.jsx)(Da,{part:e,onApprove:t,onDeny:n,onAlwaysApprove:r,isApproving:i?.has(e.toolCallId)},e.toolCallId))]})}function ka({message:e,onApprove:t,onDeny:n,onAlwaysApprove:r,approvingToolCallIds:i}){return e.parts?.some(e=>qr(e)&&e.text.length>0||G(e))?(0,E.jsxs)(ce.div,{initial:{opacity:0,y:10},animate:{opacity:1,y:0},className:`message-bubble ${e.role===`user`?`user`:`assistant`}`,children:[(0,E.jsx)(`div`,{className:`avatar`,children:e.role===`user`?(0,E.jsx)(Be,{size:18}):(0,E.jsx)(se,{size:18})}),(0,E.jsx)(`div`,{className:`content`,children:Ea(e.parts).map(e=>{if(e.kind===`tool-group`)return(0,E.jsx)(Oa,{parts:e.parts,onApprove:t,onDeny:n,onAlwaysApprove:r,approvingToolCallIds:i},e.parts[0].toolCallId);let{part:a,index:o}=e;return qr(a)?(0,E.jsx)(`div`,{className:`text-block`,children:a.text},o):G(a)?(0,E.jsx)(Da,{part:a,onApprove:t,onDeny:n,onAlwaysApprove:r,isApproving:i?.has(a.toolCallId)},o):null})})]},e.id):null}function Aa(e,t){if(e!==`submitted`&&e!==`streaming`)return!1;if(t?.role!==`assistant`)return!0;let n=t.parts?.at(-1);return n?qr(n)?n.state!==`streaming`||n.text.length===0:!G(n)||n.state===`output-available`||n.state===`output-error`:!0}function ja(){return(0,E.jsxs)(ce.div,{initial:{opacity:0,y:10},animate:{opacity:1,y:0},transition:{delay:.2},className:`message-bubble assistant`,children:[(0,E.jsx)(`div`,{className:`avatar`,children:(0,E.jsx)(se,{size:18})}),(0,E.jsxs)(`div`,{className:`content thinking-indicator`,role:`status`,"aria-label":`Harper Agent is thinking`,children:[(0,E.jsx)(`span`,{className:`thinking-dot`}),(0,E.jsx)(`span`,{className:`thinking-dot`}),(0,E.jsx)(`span`,{className:`thinking-dot`})]})]})}function Ma(e){return ie({queryKey:[`getMyUsage`,e],queryFn:async()=>{let{data:t}=await n.get(`/Chat/Usage/${e}`);return t}})}function Na(){let{organizationId:e}=te({strict:!1});return re(Ma(e))}function Pa(){let{data:e,isLoading:t,error:n}=Na();if(t||n||!e)return null;let{usageUSD:r,monthlyLimitUSD:i,usageBarPercent:a}=e,o=e=>new Intl.NumberFormat(`en-US`,{style:`currency`,currency:`USD`}).format(e);return(0,E.jsxs)(`div`,{className:`usage-container`,children:[(0,E.jsxs)(`div`,{className:`usage-info`,children:[(0,E.jsx)(`span`,{children:`Monthly Org Usage`}),(0,E.jsxs)(`span`,{children:[o(r),` / `,o(i)]}),(0,E.jsxs)(`span`,{children:[Math.round(a),`%`]})]}),(0,E.jsx)(`div`,{className:`usage-bar-bg`,children:(0,E.jsx)(`div`,{className:`usage-bar-fill`,style:{width:`${a}%`}})})]})}function Fa({autoFocus:e,closeChat:t}){let n=te({strict:!1}),{organizationId:r}=n,[i,a]=Ae(`ApplicationChat`,``),[o,s]=(0,T.useState)(!0),[c,l]=(0,T.useState)({}),[u,d]=(0,T.useState)(new Set),[f,p]=Ee(De.ChatAlwaysApprovedTools,[]),m=new Set(f),h=nt(),g=Fe(),_=ne(),{messages:v,sendMessage:ee,status:y,addToolOutput:b,setMessages:x}=vi({transport:si(r),generateId:A(),sendAutomaticallyWhen:oi,onFinish(){_.invalidateQueries({queryKey:[`getMyUsage`]})},async onToolCall({toolCall:e}){if(e.dynamic)return;let t=wa(e.toolName);if(t){if(t.requiresApproval&&!m.has(e.toolName)){let t={type:`tool-call`,toolCallId:e.toolCallId,toolName:e.toolName,input:e.input};l(n=>({...n,[e.toolCallId]:t}));return}let r=await t.execute({input:e.input,instanceClientParams:g,baseURL:h,params:n});b({tool:e.toolName,toolCallId:e.toolCallId,output:r})}}}),S=(0,T.useCallback)(async e=>{let t=c[e];if(t){d(t=>{let n=new Set(t);return n.add(e),n});try{let r=wa(t.toolName);if(r){let i=await r.execute({input:t.input,instanceClientParams:g,baseURL:h,params:n});b({tool:t.toolName,toolCallId:t.toolCallId,output:i}),l(t=>{let n={...t};return delete n[e],n})}}finally{d(t=>{let n=new Set(t);return n.delete(e),n})}}},[c,g,h,b,n]),re=(0,T.useCallback)(e=>{let t=c[e];t&&(b({tool:t.toolName,toolCallId:t.toolCallId,output:{error:`User denied the tool execution.`}}),l(t=>{let n={...t};return delete n[e],n}))},[c,b]),ie=(0,T.useCallback)(async e=>{let t=c[e];t&&(p(e=>Re([...e,t.toolName])),await S(e))},[c,p,S]);(0,T.useEffect)(()=>{(async()=>{try{let e=await ht();Array.isArray(e)&&x(e)}catch(e){console.error(`Failed to fetch initial messages:`,e)}finally{s(!1)}})()},[x]);let ae=y===`streaming`||y===`submitted`,oe=(0,T.useRef)(null);return(0,T.useEffect)(()=>{oe.current?.scrollIntoView({behavior:`smooth`})},[v]),(0,E.jsxs)(`div`,{className:`flex flex-col h-full`,children:[(0,E.jsxs)(`div`,{className:`flex items-start justify-between gap-6 px-4 py-2.5 border-b border-border bg-card`,children:[(0,E.jsxs)(`div`,{className:`flex flex-col gap-1 min-w-0 flex-1`,children:[(0,E.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,E.jsx)(se,{className:`text-primary`,size:20}),(0,E.jsx)(`span`,{className:`font-semibold text-foreground`,children:`Harper Agent`})]}),(0,E.jsx)(Pa,{})]}),(0,E.jsxs)(`div`,{className:`flex items-center gap-2 shrink-0`,children:[(0,E.jsx)(mt,{setMessages:x}),(0,E.jsx)(`button`,{onClick:t,className:`p-1 hover:bg-accent rounded-md transition-colors text-muted-foreground hover:text-foreground`,title:`Close chat`,children:(0,E.jsx)(pe,{size:20})})]})]}),(0,E.jsx)(`div`,{className:`flex-1 overflow-hidden`,children:(0,E.jsxs)(`div`,{className:`chat-interface h-full w-full`,children:[(0,E.jsxs)(`div`,{className:`messages-area`,children:[o&&(0,E.jsx)(bi,{}),!o&&v.length===0&&(0,E.jsxs)(`div`,{className:`empty-state`,children:[(0,E.jsx)(se,{size:48}),(0,E.jsx)(`p`,{children:`Ask me to create a Harper app!`})]}),v.map(e=>(0,E.jsx)(ka,{message:e,onApprove:S,onDeny:re,onAlwaysApprove:ie,approvingToolCallIds:u},e.id)),Aa(y,v.at(-1))&&(0,E.jsx)(ja,{}),(0,E.jsx)(`div`,{ref:oe})]}),(0,E.jsx)(yi,{input:i,setInput:a,onSubmit:e=>{e.preventDefault(),i.trim()&&!ae&&!o&&(ee({text:i}),a(``))},disabled:o,autoFocus:e})]})})]})}export{Fa as Chat};
|
|
1949
|
-
//# sourceMappingURL=Chat-
|
|
1949
|
+
//# sourceMappingURL=Chat-DHP4XpID.js.map
|