@databricks/appkit 0.82.0 → 0.84.0
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/CLAUDE.md +21 -1
- package/dist/agents/databricks.d.ts +15 -3
- package/dist/agents/databricks.d.ts.map +1 -1
- package/dist/agents/databricks.js +25 -8
- package/dist/agents/databricks.js.map +1 -1
- package/dist/appkit/package.js +1 -1
- package/dist/beta.d.ts +2 -2
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +6 -1
- package/dist/cache/index.js.map +1 -1
- package/dist/cli/commands/agent/eval.js +1 -1
- package/dist/cli/commands/generate-types.js +1 -1
- package/dist/cli/commands/plugin/sync/sync.js +28 -15
- package/dist/cli/commands/plugin/sync/sync.js.map +1 -1
- package/dist/cli/commands/registry/add.js +3 -11
- package/dist/cli/commands/registry/add.js.map +1 -1
- package/dist/cli/commands/registry/config-writer.js +1 -1
- package/dist/connectors/lakebase/index.js +1 -1
- package/dist/connectors/lakebase/routing-pool.d.ts +1 -1
- package/dist/connectors/lakebase/routing-pool.d.ts.map +1 -1
- package/dist/connectors/lakebase/routing-pool.js +6 -12
- package/dist/connectors/lakebase/routing-pool.js.map +1 -1
- package/dist/context/caller-context.d.ts +25 -0
- package/dist/context/caller-context.d.ts.map +1 -0
- package/dist/context/caller-context.js +21 -0
- package/dist/context/caller-context.js.map +1 -0
- package/dist/context/deprecation.js +14 -0
- package/dist/context/deprecation.js.map +1 -0
- package/dist/context/execution-context.d.ts +40 -4
- package/dist/context/execution-context.d.ts.map +1 -1
- package/dist/context/execution-context.js +87 -33
- package/dist/context/execution-context.js.map +1 -1
- package/dist/context/index.d.ts +3 -2
- package/dist/context/index.js +3 -1
- package/dist/context/request-scope.d.ts +2 -0
- package/dist/context/request-scope.js +39 -0
- package/dist/context/request-scope.js.map +1 -0
- package/dist/context/resource-capabilities.js +59 -0
- package/dist/context/resource-capabilities.js.map +1 -0
- package/dist/context/scoped-api.js +104 -0
- package/dist/context/scoped-api.js.map +1 -0
- package/dist/context/service-context.d.ts +18 -12
- package/dist/context/service-context.d.ts.map +1 -1
- package/dist/context/service-context.js +45 -55
- package/dist/context/service-context.js.map +1 -1
- package/dist/context/user-context.d.ts +16 -22
- package/dist/context/user-context.d.ts.map +1 -1
- package/dist/context/user-context.js +57 -3
- package/dist/context/user-context.js.map +1 -1
- package/dist/core/agent/load-agents.d.ts.map +1 -1
- package/dist/core/agent/load-agents.js +5 -2
- package/dist/core/agent/load-agents.js.map +1 -1
- package/dist/core/agent/run-agent.d.ts +19 -6
- package/dist/core/agent/run-agent.d.ts.map +1 -1
- package/dist/core/agent/run-agent.js +53 -17
- package/dist/core/agent/run-agent.js.map +1 -1
- package/dist/core/agent/types.d.ts +18 -1
- package/dist/core/agent/types.d.ts.map +1 -1
- package/dist/core/agent/types.js.map +1 -1
- package/dist/core/appkit.d.ts +4 -3
- package/dist/core/appkit.d.ts.map +1 -1
- package/dist/core/appkit.js +34 -11
- package/dist/core/appkit.js.map +1 -1
- package/dist/core/plugin-context.d.ts +14 -15
- package/dist/core/plugin-context.d.ts.map +1 -1
- package/dist/core/plugin-context.js +24 -14
- package/dist/core/plugin-context.js.map +1 -1
- package/dist/errors/base.d.ts +2 -2
- package/dist/errors/base.js +2 -2
- package/dist/errors/base.js.map +1 -1
- package/dist/errors/identity-expired.d.ts +14 -0
- package/dist/errors/identity-expired.d.ts.map +1 -0
- package/dist/errors/identity-expired.js +33 -0
- package/dist/errors/identity-expired.js.map +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/index.d.ts +12 -6
- package/dist/index.js +7 -2
- package/dist/logging/logger.js +1 -1
- package/dist/plugin/execution-result.d.ts +4 -1
- package/dist/plugin/execution-result.d.ts.map +1 -1
- package/dist/plugin/interceptors/telemetry.js +6 -4
- package/dist/plugin/interceptors/telemetry.js.map +1 -1
- package/dist/plugin/plugin.d.ts +14 -22
- package/dist/plugin/plugin.d.ts.map +1 -1
- package/dist/plugin/plugin.js +42 -123
- package/dist/plugin/plugin.js.map +1 -1
- package/dist/plugins/agents/agents.d.ts +13 -1
- package/dist/plugins/agents/agents.d.ts.map +1 -1
- package/dist/plugins/agents/agents.js +65 -15
- package/dist/plugins/agents/agents.js.map +1 -1
- package/dist/plugins/agents/auth-mode.js +42 -0
- package/dist/plugins/agents/auth-mode.js.map +1 -0
- package/dist/plugins/agents/index.d.ts +1 -1
- package/dist/plugins/agents/mlflow.js +11 -3
- package/dist/plugins/agents/mlflow.js.map +1 -1
- package/dist/plugins/agents/tool-dispatch.js +9 -1
- package/dist/plugins/agents/tool-dispatch.js.map +1 -1
- package/dist/plugins/ai-search/ai-search.d.ts +1 -1
- package/dist/plugins/ai-search/ai-search.d.ts.map +1 -1
- package/dist/plugins/ai-search/ai-search.js +4 -4
- package/dist/plugins/ai-search/ai-search.js.map +1 -1
- package/dist/plugins/analytics/analytics.d.ts +3 -3
- package/dist/plugins/analytics/analytics.d.ts.map +1 -1
- package/dist/plugins/analytics/analytics.js +9 -7
- package/dist/plugins/analytics/analytics.js.map +1 -1
- package/dist/plugins/database/crud/contract.js +2 -2
- package/dist/plugins/database/crud/contract.js.map +1 -1
- package/dist/plugins/database/database.d.ts +1 -1
- package/dist/plugins/files/plugin.d.ts +21 -15
- package/dist/plugins/files/plugin.d.ts.map +1 -1
- package/dist/plugins/files/plugin.js +50 -29
- package/dist/plugins/files/plugin.js.map +1 -1
- package/dist/plugins/genie/genie.d.ts +10 -1
- package/dist/plugins/genie/genie.d.ts.map +1 -1
- package/dist/plugins/genie/genie.js +12 -3
- package/dist/plugins/genie/genie.js.map +1 -1
- package/dist/plugins/genie/manifest.js +1 -0
- package/dist/plugins/jobs/plugin.d.ts +1 -1
- package/dist/plugins/jobs/plugin.js +2 -2
- package/dist/plugins/jobs/plugin.js.map +1 -1
- package/dist/plugins/lakebase/lakebase.d.ts +11 -18
- package/dist/plugins/lakebase/lakebase.d.ts.map +1 -1
- package/dist/plugins/lakebase/lakebase.js +16 -21
- package/dist/plugins/lakebase/lakebase.js.map +1 -1
- package/dist/plugins/server/client-config-sanitizer.js +1 -4
- package/dist/plugins/server/client-config-sanitizer.js.map +1 -1
- package/dist/plugins/server/dev-obo-middleware.js +60 -0
- package/dist/plugins/server/dev-obo-middleware.js.map +1 -0
- package/dist/plugins/server/index.d.ts +1 -1
- package/dist/plugins/server/index.d.ts.map +1 -1
- package/dist/plugins/server/index.js +3 -0
- package/dist/plugins/server/index.js.map +1 -1
- package/dist/plugins/serving/manifest.js +1 -0
- package/dist/plugins/serving/serving.d.ts +1 -1
- package/dist/plugins/serving/serving.js +6 -6
- package/dist/plugins/serving/serving.js.map +1 -1
- package/dist/resources/index.d.ts +1 -0
- package/dist/resources/index.js +3 -0
- package/dist/resources/warehouse.d.ts +16 -0
- package/dist/resources/warehouse.d.ts.map +1 -0
- package/dist/resources/warehouse.js +93 -0
- package/dist/resources/warehouse.js.map +1 -0
- package/dist/schemas/manifest.d.ts +44 -1
- package/dist/schemas/manifest.d.ts.map +1 -1
- package/dist/schemas/manifest.js +122 -3
- package/dist/schemas/manifest.js.map +1 -1
- package/dist/shared/src/dev-obo.js +86 -0
- package/dist/shared/src/dev-obo.js.map +1 -0
- package/dist/shared/src/index.d.ts +1 -1
- package/dist/shared/src/plugin.d.ts +12 -1
- package/dist/shared/src/plugin.d.ts.map +1 -1
- package/dist/shared/src/schemas/manifest.d.ts +17 -0
- package/dist/shared/src/schemas/manifest.d.ts.map +1 -1
- package/dist/shared/src/schemas/manifest.js +53 -3
- package/dist/shared/src/schemas/manifest.js.map +1 -1
- package/dist/stream/stream-manager.d.ts.map +1 -1
- package/dist/stream/stream-manager.js +5 -2
- package/dist/stream/stream-manager.js.map +1 -1
- package/dist/telemetry/execution-span-processor.js +21 -0
- package/dist/telemetry/execution-span-processor.js.map +1 -0
- package/dist/telemetry/telemetry-manager.js +2 -1
- package/dist/telemetry/telemetry-manager.js.map +1 -1
- package/dist/testing/create-test-app.d.ts +2 -2
- package/dist/testing/create-test-app.js +2 -2
- package/dist/testing/create-test-app.js.map +1 -1
- package/dist/testing/fixtures.d.ts +3 -2
- package/dist/testing/fixtures.d.ts.map +1 -1
- package/dist/testing/fixtures.js +21 -14
- package/dist/testing/fixtures.js.map +1 -1
- package/dist/testing/reset-singletons.js +1 -1
- package/dist/testing/test-plugin-context.d.ts +3 -12
- package/dist/testing/test-plugin-context.d.ts.map +1 -1
- package/dist/testing/test-plugin-context.js +24 -16
- package/dist/testing/test-plugin-context.js.map +1 -1
- package/dist/utils/is-plain-object.js +11 -0
- package/dist/utils/is-plain-object.js.map +1 -0
- package/docs/api/appkit/Class.AppKitError.md +2 -1
- package/docs/api/appkit/Class.AuthenticationError.md +1 -1
- package/docs/api/appkit/Class.ConfigurationError.md +1 -1
- package/docs/api/appkit/Class.ConnectionError.md +1 -1
- package/docs/api/appkit/Class.DatabaseValidationError.md +1 -1
- package/docs/api/appkit/Class.ExecutionError.md +1 -1
- package/docs/api/appkit/Class.IdentityExpiredError.md +190 -0
- package/docs/api/appkit/Class.InitializationError.md +1 -1
- package/docs/api/appkit/Class.Plugin.md +9 -13
- package/docs/api/appkit/Class.ServerError.md +1 -1
- package/docs/api/appkit/Class.ServiceContext.md +169 -0
- package/docs/api/appkit/Class.TunnelError.md +1 -1
- package/docs/api/appkit/Class.ValidationError.md +1 -1
- package/docs/api/appkit/Function.createApp.md +12 -12
- package/docs/api/appkit/Function.getCallerContext.md +12 -0
- package/docs/api/appkit/Function.getCurrentActorId.md +12 -0
- package/docs/api/appkit/Function.getCurrentPrincipalKey.md +12 -0
- package/docs/api/appkit/Function.getCurrentUserId.md +14 -0
- package/docs/api/appkit/Function.getExecutionContext.md +5 -3
- package/docs/api/appkit/Function.getUserContext.md +16 -0
- package/docs/api/appkit/Function.getWarehouseId.md +20 -0
- package/docs/api/appkit/Function.isInUserContext.md +12 -0
- package/docs/api/appkit/Function.isUserContext.md +20 -0
- package/docs/api/appkit/Function.runAgent.md +1 -1
- package/docs/api/appkit/Function.runInCallerContext.md +27 -0
- package/docs/api/appkit/Function.runInUserContext.md +29 -0
- package/docs/api/appkit/Interface.AgentDefinition.md +11 -0
- package/docs/api/appkit/Interface.AgentsPluginConfig.md +11 -0
- package/docs/api/appkit/Interface.CallerContext.md +54 -0
- package/docs/api/appkit/Interface.IndexConfig.md +1 -1
- package/docs/api/appkit/Interface.PluginManifest.md +33 -2
- package/docs/api/appkit/Interface.RegisteredAgent.md +11 -0
- package/docs/api/appkit/Interface.RunAgentInput.md +45 -1
- package/docs/api/appkit/TypeAlias.AgentAuth.md +8 -0
- package/docs/api/appkit/TypeAlias.AppKitApi.md +35 -0
- package/docs/api/appkit/TypeAlias.CallerPrincipal.md +13 -0
- package/docs/api/appkit/TypeAlias.ExecutionContext.md +9 -0
- package/docs/api/appkit/TypeAlias.ExecutionResult.md +65 -0
- package/docs/api/appkit/TypeAlias.ScopedPluginMap.md +12 -0
- package/docs/api/appkit/TypeAlias.UserContext.md +109 -0
- package/docs/api/appkit/TypeAlias.UserScopedApp.md +39 -0
- package/docs/api/appkit.md +20 -0
- package/docs/plugins/agents.md +45 -0
- package/docs/plugins/analytics.md +8 -0
- package/docs/plugins/execution-context.md +101 -50
- package/docs/plugins/lakebase.md +6 -82
- package/llms.txt +21 -1
- package/package.json +1 -1
- package/sbom.cdx.json +1 -1
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { createLogger } from "../logging/logger.js";
|
|
2
|
+
import { ServiceContext } from "../context/service-context.js";
|
|
3
|
+
import { getCallerContext, normalizeIdentityError } from "../context/execution-context.js";
|
|
4
|
+
import { assertPluginExecution, getPluginResourceTypes } from "../context/resource-capabilities.js";
|
|
2
5
|
import { TelemetryManager } from "../telemetry/telemetry-manager.js";
|
|
3
6
|
import { SpanStatusCode } from "../telemetry/index.js";
|
|
7
|
+
import { createRequestScope } from "../context/request-scope.js";
|
|
4
8
|
import { forwardAsyncErrors } from "../utils/safe-handler.js";
|
|
5
9
|
|
|
6
10
|
//#region src/core/plugin-context.ts
|
|
@@ -25,16 +29,19 @@ var PluginContext = class {
|
|
|
25
29
|
plugins = /* @__PURE__ */ new Map();
|
|
26
30
|
lifecycleHooks = /* @__PURE__ */ new Map();
|
|
27
31
|
telemetry;
|
|
32
|
+
createCallerContext;
|
|
28
33
|
/**
|
|
29
34
|
* @param deps.telemetry - Telemetry provider used for `executeTool` spans.
|
|
30
|
-
* Defaults to the shared `"plugin-context"` provider
|
|
35
|
+
* Defaults to the shared `"plugin-context"` provider, the production
|
|
31
36
|
* path. Injectable so the testing kit can pass a mock provider and run
|
|
32
|
-
* `executeTool` without a live OpenTelemetry pipeline.
|
|
33
|
-
*
|
|
37
|
+
* `executeTool` without a live OpenTelemetry pipeline.
|
|
38
|
+
* @param deps.createCallerContext - Offline credential factory for tests.
|
|
39
|
+
* Header validation and ALS remain real; route buffering and the registry are
|
|
34
40
|
* exercised through the existing public API.
|
|
35
41
|
*/
|
|
36
42
|
constructor(deps = {}) {
|
|
37
43
|
this.telemetry = deps.telemetry ?? TelemetryManager.getProvider("plugin-context");
|
|
44
|
+
this.createCallerContext = deps.createCallerContext ?? ((...args) => ServiceContext.createCallerContext(...args));
|
|
38
45
|
}
|
|
39
46
|
/**
|
|
40
47
|
* Register a route on the root Express application.
|
|
@@ -126,15 +133,15 @@ var PluginContext = class {
|
|
|
126
133
|
}));
|
|
127
134
|
}
|
|
128
135
|
/**
|
|
129
|
-
* Execute a tool on a ToolProvider plugin with
|
|
136
|
+
* Execute a tool on a ToolProvider plugin with the ambient principal
|
|
130
137
|
* and telemetry.
|
|
131
138
|
*
|
|
132
139
|
* The context:
|
|
133
140
|
* 1. Resolves the plugin by name
|
|
134
|
-
* 2.
|
|
141
|
+
* 2. Inherits the caller, or establishes user scope from the request
|
|
135
142
|
* 3. Wraps the call in a telemetry span with a configurable timeout
|
|
136
143
|
*
|
|
137
|
-
* @param timeoutMs Per-call timeout. Defaults to 5 minutes
|
|
144
|
+
* @param timeoutMs Per-call timeout. Defaults to 5 minutes, the floor
|
|
138
145
|
* for cold SQL Warehouse round-trips, long Genie conversations, and
|
|
139
146
|
* busy serverless Lakebase queries. The agents plugin overrides this
|
|
140
147
|
* per-app via `agents({ limits: { toolCallTimeoutMs } })`.
|
|
@@ -144,14 +151,16 @@ var PluginContext = class {
|
|
|
144
151
|
if (!provider) throw new Error(`PluginContext: unknown plugin "${pluginName}". Available: ${Array.from(this.toolProviders.keys()).join(", ")}`);
|
|
145
152
|
const tracer = this.telemetry.getTracer();
|
|
146
153
|
const operationName = `executeTool:${pluginName}.${toolName}`;
|
|
147
|
-
|
|
154
|
+
const executeInCurrentScope = () => tracer.startActiveSpan(operationName, async (span) => {
|
|
148
155
|
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
149
156
|
const combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
150
157
|
try {
|
|
151
|
-
|
|
158
|
+
assertPluginExecution(provider);
|
|
159
|
+
const result = await provider.executeAgentTool(toolName, args, combinedSignal);
|
|
152
160
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
153
161
|
return result;
|
|
154
|
-
} catch (
|
|
162
|
+
} catch (caught) {
|
|
163
|
+
const error = normalizeIdentityError(caught);
|
|
155
164
|
span.setStatus({
|
|
156
165
|
code: SpanStatusCode.ERROR,
|
|
157
166
|
message: error instanceof Error ? error.message : "Tool execution failed"
|
|
@@ -162,6 +171,10 @@ var PluginContext = class {
|
|
|
162
171
|
span.end();
|
|
163
172
|
}
|
|
164
173
|
});
|
|
174
|
+
return getCallerContext() ? executeInCurrentScope() : createRequestScope(req, getPluginResourceTypes(provider), {
|
|
175
|
+
createCaller: this.createCallerContext,
|
|
176
|
+
legacy: true
|
|
177
|
+
}).run(executeInCurrentScope);
|
|
165
178
|
}
|
|
166
179
|
/**
|
|
167
180
|
* Register a lifecycle hook callback.
|
|
@@ -223,13 +236,10 @@ var PluginContext = class {
|
|
|
223
236
|
}
|
|
224
237
|
};
|
|
225
238
|
/**
|
|
226
|
-
* Type guard
|
|
227
|
-
* and exposes the user-scoped `asUser` helper that the {@link Plugin} base
|
|
228
|
-
* class provides. Narrowing to {@link ToolProviderPlugin} lets `executeTool`
|
|
229
|
-
* call `asUser` without an unsafe cast.
|
|
239
|
+
* Type guard for the tool-provider methods. Identity comes from the ALS scope.
|
|
230
240
|
*/
|
|
231
241
|
function isToolProvider(plugin) {
|
|
232
|
-
return typeof plugin === "object" && plugin !== null && "getAgentTools" in plugin && typeof plugin.getAgentTools === "function" && "executeAgentTool" in plugin && typeof plugin.executeAgentTool === "function"
|
|
242
|
+
return typeof plugin === "object" && plugin !== null && "getAgentTools" in plugin && typeof plugin.getAgentTools === "function" && "executeAgentTool" in plugin && typeof plugin.executeAgentTool === "function";
|
|
233
243
|
}
|
|
234
244
|
|
|
235
245
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-context.js","names":[],"sources":["../../src/core/plugin-context.ts"],"sourcesContent":["import type express from \"express\";\nimport type { BasePlugin, IAppRequest, ToolProvider } from \"shared\";\n\nimport { createLogger } from \"../logging/logger\";\nimport {\n type ITelemetry,\n SpanStatusCode,\n TelemetryManager,\n} from \"../telemetry\";\nimport { forwardAsyncErrors } from \"../utils/safe-handler\";\n\nconst logger = createLogger(\"plugin-context\");\n\ninterface BufferedRoute {\n method: string;\n path: string;\n handlers: express.RequestHandler[];\n}\n\ninterface RouteTarget {\n addExtension(fn: (app: express.Application) => void): void;\n}\n\n/**\n * A tool-provider plugin that also exposes user-scoped execution. Plugins\n * derived from {@link Plugin} satisfy this implicitly because `asUser` lives\n * on the base class. {@link isToolProvider} narrows to this shape so\n * `executeTool` can call `asUser` without an unsafe cast.\n */\ntype ToolProviderPlugin = BasePlugin &\n ToolProvider & { asUser: (req: IAppRequest) => ToolProvider };\n\n/**\n * Lifecycle events emitted through {@link PluginContext.emitLifecycle}.\n *\n * - `\"setup:complete\"` — emitted by AppKit core after every plugin's\n * `setup()` has finished.\n * - `\"server:ready\"` — emitted when the HTTP server is listening.\n * - `\"shutdown\"` — emitted by the core lifecycle manager during graceful\n * shutdown, AFTER all plugin `shutdown()` hooks have completed. The emit is\n * bounded by a short timeout (see the lifecycle manager's shutdown budget),\n * so subscribers must not start long-running async work — finish quickly or\n * be cut off. (The server plugin subscribes here to force-close its\n * remaining sockets once peers have drained.)\n */\ntype LifecycleEvent = \"setup:complete\" | \"server:ready\" | \"shutdown\";\n\n/**\n * Mediator for inter-plugin communication.\n *\n * Created by AppKit core and passed to every plugin. Plugins request\n * capabilities from the context instead of holding direct references\n * to sibling plugin instances.\n *\n * Capabilities:\n * - Route mounting with buffering (order-independent)\n * - Typed ToolProvider registry (live, not snapshot-based)\n * - User-scoped tool execution with automatic telemetry\n * - Lifecycle hooks for plugin coordination\n */\nexport class PluginContext {\n private routeBuffer: BufferedRoute[] = [];\n private routeTarget: RouteTarget | null = null;\n private toolProviders = new Map<string, ToolProviderPlugin>();\n private plugins = new Map<string, BasePlugin>();\n private lifecycleHooks = new Map<\n LifecycleEvent,\n Set<() => void | Promise<void>>\n >();\n private telemetry: ITelemetry;\n\n /**\n * @param deps.telemetry - Telemetry provider used for `executeTool` spans.\n * Defaults to the shared `\"plugin-context\"` provider — the production\n * path. Injectable so the testing kit can pass a mock provider and run\n * `executeTool` without a live OpenTelemetry pipeline. This is the only\n * seam the mock context needs; route buffering and the tool registry are\n * exercised through the existing public API.\n */\n constructor(deps: { telemetry?: ITelemetry } = {}) {\n this.telemetry =\n deps.telemetry ?? TelemetryManager.getProvider(\"plugin-context\");\n }\n\n /**\n * Register a route on the root Express application.\n *\n * If a route target (server plugin) has registered, the route is applied\n * immediately. Otherwise it is buffered and flushed when a route target\n * becomes available.\n */\n addRoute(\n method: string,\n path: string,\n ...handlers: express.RequestHandler[]\n ): void {\n if (this.routeTarget) {\n this.applyRoute({ method, path, handlers });\n } else {\n this.routeBuffer.push({ method, path, handlers });\n }\n }\n\n /**\n * Register middleware on the root Express application.\n *\n * Same buffering semantics as `addRoute`.\n */\n addMiddleware(path: string, ...handlers: express.RequestHandler[]): void {\n if (this.routeTarget) {\n this.applyMiddleware(path, handlers);\n } else {\n this.routeBuffer.push({ method: \"use\", path, handlers });\n }\n }\n\n /**\n * Called by the server plugin to opt in as the route target.\n * Flushes all buffered routes via the server's `addExtension`.\n *\n * Only the first caller wins — subsequent calls are ignored with a warning.\n * In practice only the server plugin registers, but a misconfigured app\n * (two server plugins, or duplicate AppKit setup in tests) would otherwise\n * silently drop the first target's later extensions.\n */\n registerAsRouteTarget(target: RouteTarget): void {\n if (this.routeTarget) {\n logger.warn(\n \"registerAsRouteTarget called more than once; ignoring duplicate registration\",\n );\n return;\n }\n this.routeTarget = target;\n\n for (const route of this.routeBuffer) {\n if (route.method === \"use\") {\n this.applyMiddleware(route.path, route.handlers);\n } else {\n this.applyRoute(route);\n }\n }\n this.routeBuffer = [];\n }\n\n /**\n * Register a plugin that implements the ToolProvider interface.\n * Called by AppKit core after constructing each plugin.\n *\n * Plugin names should be unique (they are derived from `manifest.name`).\n * A duplicate registration overwrites the previous entry and emits a\n * warning so the misconfiguration is visible in startup logs.\n */\n registerToolProvider(name: string, plugin: ToolProviderPlugin): void {\n if (this.toolProviders.has(name)) {\n logger.warn(\n 'Tool provider \"%s\" registered more than once; the previous registration is being overwritten',\n name,\n );\n }\n this.toolProviders.set(name, plugin);\n }\n\n /**\n * Register a plugin instance.\n * Called by AppKit core after constructing each plugin.\n */\n registerPlugin(name: string, instance: BasePlugin): void {\n this.plugins.set(name, instance);\n }\n\n /**\n * Returns all registered plugin instances keyed by name.\n * Used by the server plugin for route injection, client config,\n * and shutdown coordination. The returned map is read-only at the\n * type level — callers must not mutate the live registry.\n */\n getPlugins(): ReadonlyMap<string, BasePlugin> {\n return this.plugins;\n }\n\n /**\n * Returns all registered ToolProvider plugins.\n * Always returns the current set — not a frozen snapshot.\n */\n getToolProviders(): Array<{ name: string; provider: ToolProvider }> {\n return Array.from(this.toolProviders.entries()).map(([name, provider]) => ({\n name,\n provider,\n }));\n }\n\n /**\n * Execute a tool on a ToolProvider plugin with automatic user scoping\n * and telemetry.\n *\n * The context:\n * 1. Resolves the plugin by name\n * 2. Calls `asUser(req)` for user-scoped execution\n * 3. Wraps the call in a telemetry span with a configurable timeout\n *\n * @param timeoutMs Per-call timeout. Defaults to 5 minutes — the floor\n * for cold SQL Warehouse round-trips, long Genie conversations, and\n * busy serverless Lakebase queries. The agents plugin overrides this\n * per-app via `agents({ limits: { toolCallTimeoutMs } })`.\n */\n async executeTool(\n req: express.Request,\n pluginName: string,\n toolName: string,\n args: unknown,\n signal?: AbortSignal,\n timeoutMs: number = 300_000,\n ): Promise<unknown> {\n const provider = this.toolProviders.get(pluginName);\n if (!provider) {\n throw new Error(\n `PluginContext: unknown plugin \"${pluginName}\". Available: ${Array.from(this.toolProviders.keys()).join(\", \")}`,\n );\n }\n\n const tracer = this.telemetry.getTracer();\n const operationName = `executeTool:${pluginName}.${toolName}`;\n\n return tracer.startActiveSpan(operationName, async (span) => {\n const timeoutSignal = AbortSignal.timeout(timeoutMs);\n const combinedSignal = signal\n ? AbortSignal.any([signal, timeoutSignal])\n : timeoutSignal;\n\n try {\n const userScoped = provider.asUser(req);\n const result = await userScoped.executeAgentTool(\n toolName,\n args,\n combinedSignal,\n );\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (error) {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message:\n error instanceof Error ? error.message : \"Tool execution failed\",\n });\n span.recordException(\n error instanceof Error ? error : new Error(String(error)),\n );\n throw error;\n } finally {\n span.end();\n }\n });\n }\n\n /**\n * Register a lifecycle hook callback.\n *\n * See {@link LifecycleEvent} for event semantics. In particular,\n * `\"shutdown\"` subscribers run inside a bounded shutdown phase and must\n * not start long-running async work.\n */\n onLifecycle(event: LifecycleEvent, fn: () => void | Promise<void>): void {\n let hooks = this.lifecycleHooks.get(event);\n if (!hooks) {\n hooks = new Set();\n this.lifecycleHooks.set(event, hooks);\n }\n hooks.add(fn);\n }\n\n /**\n * Emit a lifecycle event, calling all registered callbacks.\n * Errors in individual callbacks are logged but do not prevent\n * other callbacks from running.\n *\n * @internal Called by AppKit core: `setup:complete` after plugin setup,\n * and `shutdown` by the lifecycle manager during graceful shutdown.\n */\n async emitLifecycle(event: LifecycleEvent): Promise<void> {\n const hooks = this.lifecycleHooks.get(event);\n if (!hooks) return;\n\n if (\n event === \"setup:complete\" &&\n this.routeBuffer.length > 0 &&\n !this.routeTarget\n ) {\n logger.warn(\n \"%d buffered routes were never applied — no server plugin registered as route target\",\n this.routeBuffer.length,\n );\n }\n\n // Snapshot before iterating so a callback that registers a new hook for\n // the same event does not mutate the loop. ECMAScript Set iteration would\n // otherwise visit late-added entries, risking unexpected re-entry.\n for (const fn of [...hooks]) {\n try {\n await fn();\n } catch (error) {\n logger.error(\"Lifecycle hook '%s' failed: %O\", event, error);\n }\n }\n }\n\n /**\n * Returns all registered plugin names.\n */\n getPluginNames(): string[] {\n return Array.from(this.plugins.keys());\n }\n\n /**\n * Check if a plugin with the given name is registered.\n */\n hasPlugin(name: string): boolean {\n return this.plugins.has(name);\n }\n\n private applyRoute(route: BufferedRoute): void {\n if (!this.routeTarget) return;\n this.routeTarget.addExtension((app) => {\n const method = route.method.toLowerCase() as keyof express.Application;\n if (typeof app[method] === \"function\") {\n (app[method] as (...a: unknown[]) => void)(\n route.path,\n ...route.handlers.map(forwardAsyncErrors),\n );\n }\n });\n }\n\n private applyMiddleware(\n path: string,\n handlers: express.RequestHandler[],\n ): void {\n if (!this.routeTarget) return;\n this.routeTarget.addExtension((app) => {\n app.use(path, ...handlers.map(forwardAsyncErrors));\n });\n }\n}\n\n/**\n * Type guard: checks whether a plugin implements the ToolProvider interface\n * and exposes the user-scoped `asUser` helper that the {@link Plugin} base\n * class provides. Narrowing to {@link ToolProviderPlugin} lets `executeTool`\n * call `asUser` without an unsafe cast.\n */\nexport function isToolProvider(plugin: unknown): plugin is ToolProviderPlugin {\n return (\n typeof plugin === \"object\" &&\n plugin !== null &&\n \"getAgentTools\" in plugin &&\n typeof (plugin as ToolProvider).getAgentTools === \"function\" &&\n \"executeAgentTool\" in plugin &&\n typeof (plugin as ToolProvider).executeAgentTool === \"function\" &&\n \"asUser\" in plugin &&\n typeof (plugin as { asUser?: unknown }).asUser === \"function\"\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin-context.js","names":[],"sources":["../../src/core/plugin-context.ts"],"sourcesContent":["import type express from \"express\";\nimport type { BasePlugin, ToolProvider } from \"shared\";\n\nimport {\n getCallerContext,\n normalizeIdentityError,\n} from \"../context/execution-context\";\nimport { createRequestScope } from \"../context/request-scope\";\nimport {\n assertPluginExecution,\n getPluginResourceTypes,\n} from \"../context/resource-capabilities\";\nimport { ServiceContext } from \"../context/service-context\";\nimport { createLogger } from \"../logging/logger\";\nimport {\n type ITelemetry,\n SpanStatusCode,\n TelemetryManager,\n} from \"../telemetry\";\nimport { forwardAsyncErrors } from \"../utils/safe-handler\";\n\nconst logger = createLogger(\"plugin-context\");\n\ninterface BufferedRoute {\n method: string;\n path: string;\n handlers: express.RequestHandler[];\n}\n\ninterface RouteTarget {\n addExtension(fn: (app: express.Application) => void): void;\n}\n\n/**\n * Tool execution inherits the caller scope established by the entry point.\n */\ntype ToolProviderPlugin = BasePlugin & ToolProvider;\n\n/**\n * Lifecycle events emitted through {@link PluginContext.emitLifecycle}.\n *\n * - `\"setup:complete\"` — emitted by AppKit core after every plugin's\n * `setup()` has finished.\n * - `\"server:ready\"` — emitted when the HTTP server is listening.\n * - `\"shutdown\"` — emitted by the core lifecycle manager during graceful\n * shutdown, AFTER all plugin `shutdown()` hooks have completed. The emit is\n * bounded by a short timeout (see the lifecycle manager's shutdown budget),\n * so subscribers must not start long-running async work — finish quickly or\n * be cut off. (The server plugin subscribes here to force-close its\n * remaining sockets once peers have drained.)\n */\ntype LifecycleEvent = \"setup:complete\" | \"server:ready\" | \"shutdown\";\n\n/**\n * Mediator for inter-plugin communication.\n *\n * Created by AppKit core and passed to every plugin. Plugins request\n * capabilities from the context instead of holding direct references\n * to sibling plugin instances.\n *\n * Capabilities:\n * - Route mounting with buffering (order-independent)\n * - Typed ToolProvider registry (live, not snapshot-based)\n * - User-scoped tool execution with automatic telemetry\n * - Lifecycle hooks for plugin coordination\n */\nexport class PluginContext {\n private routeBuffer: BufferedRoute[] = [];\n private routeTarget: RouteTarget | null = null;\n private toolProviders = new Map<string, ToolProviderPlugin>();\n private plugins = new Map<string, BasePlugin>();\n private lifecycleHooks = new Map<\n LifecycleEvent,\n Set<() => void | Promise<void>>\n >();\n private telemetry: ITelemetry;\n private createCallerContext: typeof ServiceContext.createCallerContext;\n\n /**\n * @param deps.telemetry - Telemetry provider used for `executeTool` spans.\n * Defaults to the shared `\"plugin-context\"` provider, the production\n * path. Injectable so the testing kit can pass a mock provider and run\n * `executeTool` without a live OpenTelemetry pipeline.\n * @param deps.createCallerContext - Offline credential factory for tests.\n * Header validation and ALS remain real; route buffering and the registry are\n * exercised through the existing public API.\n */\n constructor(\n deps: {\n telemetry?: ITelemetry;\n /** @internal Credential factory seam for offline testing. */\n createCallerContext?: typeof ServiceContext.createCallerContext;\n } = {},\n ) {\n this.telemetry =\n deps.telemetry ?? TelemetryManager.getProvider(\"plugin-context\");\n this.createCallerContext =\n deps.createCallerContext ??\n ((...args) => ServiceContext.createCallerContext(...args));\n }\n\n /**\n * Register a route on the root Express application.\n *\n * If a route target (server plugin) has registered, the route is applied\n * immediately. Otherwise it is buffered and flushed when a route target\n * becomes available.\n */\n addRoute(\n method: string,\n path: string,\n ...handlers: express.RequestHandler[]\n ): void {\n if (this.routeTarget) {\n this.applyRoute({ method, path, handlers });\n } else {\n this.routeBuffer.push({ method, path, handlers });\n }\n }\n\n /**\n * Register middleware on the root Express application.\n *\n * Same buffering semantics as `addRoute`.\n */\n addMiddleware(path: string, ...handlers: express.RequestHandler[]): void {\n if (this.routeTarget) {\n this.applyMiddleware(path, handlers);\n } else {\n this.routeBuffer.push({ method: \"use\", path, handlers });\n }\n }\n\n /**\n * Called by the server plugin to opt in as the route target.\n * Flushes all buffered routes via the server's `addExtension`.\n *\n * Only the first caller wins — subsequent calls are ignored with a warning.\n * In practice only the server plugin registers, but a misconfigured app\n * (two server plugins, or duplicate AppKit setup in tests) would otherwise\n * silently drop the first target's later extensions.\n */\n registerAsRouteTarget(target: RouteTarget): void {\n if (this.routeTarget) {\n logger.warn(\n \"registerAsRouteTarget called more than once; ignoring duplicate registration\",\n );\n return;\n }\n this.routeTarget = target;\n\n for (const route of this.routeBuffer) {\n if (route.method === \"use\") {\n this.applyMiddleware(route.path, route.handlers);\n } else {\n this.applyRoute(route);\n }\n }\n this.routeBuffer = [];\n }\n\n /**\n * Register a plugin that implements the ToolProvider interface.\n * Called by AppKit core after constructing each plugin.\n *\n * Plugin names should be unique (they are derived from `manifest.name`).\n * A duplicate registration overwrites the previous entry and emits a\n * warning so the misconfiguration is visible in startup logs.\n */\n registerToolProvider(name: string, plugin: ToolProviderPlugin): void {\n if (this.toolProviders.has(name)) {\n logger.warn(\n 'Tool provider \"%s\" registered more than once; the previous registration is being overwritten',\n name,\n );\n }\n this.toolProviders.set(name, plugin);\n }\n\n /**\n * Register a plugin instance.\n * Called by AppKit core after constructing each plugin.\n */\n registerPlugin(name: string, instance: BasePlugin): void {\n this.plugins.set(name, instance);\n }\n\n /**\n * Returns all registered plugin instances keyed by name.\n * Used by the server plugin for route injection, client config,\n * and shutdown coordination. The returned map is read-only at the\n * type level — callers must not mutate the live registry.\n */\n getPlugins(): ReadonlyMap<string, BasePlugin> {\n return this.plugins;\n }\n\n /**\n * Returns all registered ToolProvider plugins.\n * Always returns the current set — not a frozen snapshot.\n */\n getToolProviders(): Array<{ name: string; provider: ToolProvider }> {\n return Array.from(this.toolProviders.entries()).map(([name, provider]) => ({\n name,\n provider,\n }));\n }\n\n /**\n * Execute a tool on a ToolProvider plugin with the ambient principal\n * and telemetry.\n *\n * The context:\n * 1. Resolves the plugin by name\n * 2. Inherits the caller, or establishes user scope from the request\n * 3. Wraps the call in a telemetry span with a configurable timeout\n *\n * @param timeoutMs Per-call timeout. Defaults to 5 minutes, the floor\n * for cold SQL Warehouse round-trips, long Genie conversations, and\n * busy serverless Lakebase queries. The agents plugin overrides this\n * per-app via `agents({ limits: { toolCallTimeoutMs } })`.\n */\n async executeTool(\n req: express.Request,\n pluginName: string,\n toolName: string,\n args: unknown,\n signal?: AbortSignal,\n timeoutMs: number = 300_000,\n ): Promise<unknown> {\n const provider = this.toolProviders.get(pluginName);\n if (!provider) {\n throw new Error(\n `PluginContext: unknown plugin \"${pluginName}\". Available: ${Array.from(this.toolProviders.keys()).join(\", \")}`,\n );\n }\n\n const tracer = this.telemetry.getTracer();\n const operationName = `executeTool:${pluginName}.${toolName}`;\n\n const executeInCurrentScope = () =>\n tracer.startActiveSpan(operationName, async (span) => {\n const timeoutSignal = AbortSignal.timeout(timeoutMs);\n const combinedSignal = signal\n ? AbortSignal.any([signal, timeoutSignal])\n : timeoutSignal;\n\n try {\n assertPluginExecution(provider);\n const result = await provider.executeAgentTool(\n toolName,\n args,\n combinedSignal,\n );\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (caught) {\n const error = normalizeIdentityError(caught);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message:\n error instanceof Error ? error.message : \"Tool execution failed\",\n });\n span.recordException(\n error instanceof Error ? error : new Error(String(error)),\n );\n throw error;\n } finally {\n span.end();\n }\n });\n\n // Inherit the caller or establish request user scope before the span and tool run.\n return getCallerContext()\n ? executeInCurrentScope()\n : createRequestScope(req, getPluginResourceTypes(provider), {\n createCaller: this.createCallerContext,\n legacy: true,\n }).run(executeInCurrentScope);\n }\n\n /**\n * Register a lifecycle hook callback.\n *\n * See {@link LifecycleEvent} for event semantics. In particular,\n * `\"shutdown\"` subscribers run inside a bounded shutdown phase and must\n * not start long-running async work.\n */\n onLifecycle(event: LifecycleEvent, fn: () => void | Promise<void>): void {\n let hooks = this.lifecycleHooks.get(event);\n if (!hooks) {\n hooks = new Set();\n this.lifecycleHooks.set(event, hooks);\n }\n hooks.add(fn);\n }\n\n /**\n * Emit a lifecycle event, calling all registered callbacks.\n * Errors in individual callbacks are logged but do not prevent\n * other callbacks from running.\n *\n * @internal Called by AppKit core: `setup:complete` after plugin setup,\n * and `shutdown` by the lifecycle manager during graceful shutdown.\n */\n async emitLifecycle(event: LifecycleEvent): Promise<void> {\n const hooks = this.lifecycleHooks.get(event);\n if (!hooks) return;\n\n if (\n event === \"setup:complete\" &&\n this.routeBuffer.length > 0 &&\n !this.routeTarget\n ) {\n logger.warn(\n \"%d buffered routes were never applied — no server plugin registered as route target\",\n this.routeBuffer.length,\n );\n }\n\n // Snapshot before iterating so a callback that registers a new hook for\n // the same event does not mutate the loop. ECMAScript Set iteration would\n // otherwise visit late-added entries, risking unexpected re-entry.\n for (const fn of [...hooks]) {\n try {\n await fn();\n } catch (error) {\n logger.error(\"Lifecycle hook '%s' failed: %O\", event, error);\n }\n }\n }\n\n /**\n * Returns all registered plugin names.\n */\n getPluginNames(): string[] {\n return Array.from(this.plugins.keys());\n }\n\n /**\n * Check if a plugin with the given name is registered.\n */\n hasPlugin(name: string): boolean {\n return this.plugins.has(name);\n }\n\n private applyRoute(route: BufferedRoute): void {\n if (!this.routeTarget) return;\n this.routeTarget.addExtension((app) => {\n const method = route.method.toLowerCase() as keyof express.Application;\n if (typeof app[method] === \"function\") {\n (app[method] as (...a: unknown[]) => void)(\n route.path,\n ...route.handlers.map(forwardAsyncErrors),\n );\n }\n });\n }\n\n private applyMiddleware(\n path: string,\n handlers: express.RequestHandler[],\n ): void {\n if (!this.routeTarget) return;\n this.routeTarget.addExtension((app) => {\n app.use(path, ...handlers.map(forwardAsyncErrors));\n });\n }\n}\n\n/**\n * Type guard for the tool-provider methods. Identity comes from the ALS scope.\n */\nexport function isToolProvider(plugin: unknown): plugin is ToolProviderPlugin {\n return (\n typeof plugin === \"object\" &&\n plugin !== null &&\n \"getAgentTools\" in plugin &&\n typeof (plugin as ToolProvider).getAgentTools === \"function\" &&\n \"executeAgentTool\" in plugin &&\n typeof (plugin as ToolProvider).executeAgentTool === \"function\"\n );\n}\n"],"mappings":"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"}
|
package/dist/errors/base.d.ts
CHANGED
|
@@ -33,8 +33,8 @@ declare abstract class AppKitError extends Error {
|
|
|
33
33
|
readonly context?: Record<string, unknown>;
|
|
34
34
|
/**
|
|
35
35
|
* Client-safe error message. When set, callers serializing the error to
|
|
36
|
-
* a client (SSE, HTTP body) MUST prefer `clientMessage` over `message
|
|
37
|
-
*
|
|
36
|
+
* a client (SSE, HTTP body) MUST prefer `clientMessage` over `message`.
|
|
37
|
+
* `message` may contain raw upstream / SDK text including statement
|
|
38
38
|
* fragments, internal object names, and correlation IDs.
|
|
39
39
|
*
|
|
40
40
|
* Subclasses can set this in their constructor for a fixed sanitized
|
package/dist/errors/base.js
CHANGED
|
@@ -37,8 +37,8 @@ var AppKitError = class extends Error {
|
|
|
37
37
|
context;
|
|
38
38
|
/**
|
|
39
39
|
* Client-safe error message. When set, callers serializing the error to
|
|
40
|
-
* a client (SSE, HTTP body) MUST prefer `clientMessage` over `message
|
|
41
|
-
*
|
|
40
|
+
* a client (SSE, HTTP body) MUST prefer `clientMessage` over `message`.
|
|
41
|
+
* `message` may contain raw upstream / SDK text including statement
|
|
42
42
|
* fragments, internal object names, and correlation IDs.
|
|
43
43
|
*
|
|
44
44
|
* Subclasses can set this in their constructor for a fixed sanitized
|
package/dist/errors/base.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","names":[],"sources":["../../src/errors/base.ts"],"sourcesContent":["/** Fields that should be redacted in logs/serialization for security */\nconst SENSITIVE_FIELD_PATTERNS = [\n /token/i,\n /password/i,\n /secret/i,\n /credential/i,\n /auth/i,\n /key$/i,\n /apikey/i,\n];\n\n/**\n * Base error class for all AppKit errors.\n * Provides a consistent structure for error handling across the framework.\n *\n * @example\n * ```typescript\n * // Catching errors by type\n * try {\n * await lakebase.query(\"...\");\n * } catch (e) {\n * if (e instanceof AuthenticationError) {\n * // Re-authenticate\n * } else if (e instanceof ConnectionError && e.isRetryable) {\n * // Retry with backoff\n * }\n * }\n *\n * // Logging errors\n * console.error(error.toJSON()); // Safe for logging, sensitive values redacted\n * ```\n */\nexport abstract class AppKitError extends Error {\n /** Error code for programmatic error handling */\n abstract readonly code: string;\n\n /** HTTP status code suggestion (can be overridden) */\n abstract readonly statusCode: number;\n\n /** Whether this error type is generally safe to retry */\n abstract readonly isRetryable: boolean;\n\n /** Optional cause of the error */\n readonly cause?: Error;\n\n /** Additional context for the error */\n readonly context?: Record<string, unknown>;\n\n /**\n * Client-safe error message. When set, callers serializing the error to\n * a client (SSE, HTTP body) MUST prefer `clientMessage` over `message
|
|
1
|
+
{"version":3,"file":"base.js","names":[],"sources":["../../src/errors/base.ts"],"sourcesContent":["/** Fields that should be redacted in logs/serialization for security */\nconst SENSITIVE_FIELD_PATTERNS = [\n /token/i,\n /password/i,\n /secret/i,\n /credential/i,\n /auth/i,\n /key$/i,\n /apikey/i,\n];\n\n/**\n * Base error class for all AppKit errors.\n * Provides a consistent structure for error handling across the framework.\n *\n * @example\n * ```typescript\n * // Catching errors by type\n * try {\n * await lakebase.query(\"...\");\n * } catch (e) {\n * if (e instanceof AuthenticationError) {\n * // Re-authenticate\n * } else if (e instanceof ConnectionError && e.isRetryable) {\n * // Retry with backoff\n * }\n * }\n *\n * // Logging errors\n * console.error(error.toJSON()); // Safe for logging, sensitive values redacted\n * ```\n */\nexport abstract class AppKitError extends Error {\n /** Error code for programmatic error handling */\n abstract readonly code: string;\n\n /** HTTP status code suggestion (can be overridden) */\n abstract readonly statusCode: number;\n\n /** Whether this error type is generally safe to retry */\n abstract readonly isRetryable: boolean;\n\n /** Optional cause of the error */\n readonly cause?: Error;\n\n /** Additional context for the error */\n readonly context?: Record<string, unknown>;\n\n /**\n * Client-safe error message. When set, callers serializing the error to\n * a client (SSE, HTTP body) MUST prefer `clientMessage` over `message`.\n * `message` may contain raw upstream / SDK text including statement\n * fragments, internal object names, and correlation IDs.\n *\n * Subclasses can set this in their constructor for a fixed sanitized\n * string. When unset, `clientMessage` defaults to a generic per-code\n * string (see the getter), and the raw `message` is kept server-side\n * only.\n */\n protected readonly _clientMessage?: string;\n\n constructor(\n message: string,\n options?: {\n cause?: Error;\n context?: Record<string, unknown>;\n clientMessage?: string;\n },\n ) {\n super(message);\n this.name = this.constructor.name;\n this.cause = options?.cause;\n this.context = options?.context;\n this._clientMessage = options?.clientMessage;\n\n // Maintains proper stack trace for where the error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n\n /**\n * Sanitized message safe to forward to clients. Override in subclasses\n * if a more specific default is appropriate.\n */\n get clientMessage(): string {\n return this._clientMessage ?? \"An internal error occurred\";\n }\n\n /**\n * Convert error to JSON for logging/serialization.\n * Sensitive values in context are automatically redacted.\n */\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n statusCode: this.statusCode,\n isRetryable: this.isRetryable,\n context: this.sanitizeContext(this.context),\n cause: this.cause?.message,\n stack: this.stack,\n };\n }\n\n /**\n * Create a human-readable string representation\n */\n toString(): string {\n let str = `${this.name} [${this.code}]: ${this.message}`;\n if (this.cause) {\n str += ` (caused by: ${this.cause.message})`;\n }\n return str;\n }\n\n /**\n * Sanitize context by redacting sensitive field values\n */\n private sanitizeContext(\n context?: Record<string, unknown>,\n ): Record<string, unknown> | undefined {\n if (!context) return undefined;\n\n const sanitized: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(context)) {\n if (this.isSensitiveField(key)) {\n sanitized[key] = \"[REDACTED]\";\n } else if (typeof value === \"object\" && value !== null) {\n // Don't deep-sanitize nested objects, just indicate their type\n sanitized[key] = Array.isArray(value)\n ? `[Array(${value.length})]`\n : \"[Object]\";\n } else {\n sanitized[key] = value;\n }\n }\n return sanitized;\n }\n\n /**\n * Check if a field name matches sensitive patterns\n */\n private isSensitiveField(fieldName: string): boolean {\n return SENSITIVE_FIELD_PATTERNS.some((pattern) => pattern.test(fieldName));\n }\n}\n"],"mappings":";;AACA,MAAM,2BAA2B;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;;;;;;;;;;;;;;AAuBD,IAAsB,cAAtB,cAA0C,MAAM;;CAW9C,AAAS;;CAGT,AAAS;;;;;;;;;;;;CAaT,AAAmB;CAEnB,YACE,SACA,SAKA;AACA,QAAM,QAAQ;AACd,OAAK,OAAO,KAAK,YAAY;AAC7B,OAAK,QAAQ,SAAS;AACtB,OAAK,UAAU,SAAS;AACxB,OAAK,iBAAiB,SAAS;AAG/B,MAAI,MAAM,kBACR,OAAM,kBAAkB,MAAM,KAAK,YAAY;;;;;;CAQnD,IAAI,gBAAwB;AAC1B,SAAO,KAAK,kBAAkB;;;;;;CAOhC,SAAkC;AAChC,SAAO;GACL,MAAM,KAAK;GACX,MAAM,KAAK;GACX,SAAS,KAAK;GACd,YAAY,KAAK;GACjB,aAAa,KAAK;GAClB,SAAS,KAAK,gBAAgB,KAAK,QAAQ;GAC3C,OAAO,KAAK,OAAO;GACnB,OAAO,KAAK;GACb;;;;;CAMH,WAAmB;EACjB,IAAI,MAAM,GAAG,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,KAAK;AAC/C,MAAI,KAAK,MACP,QAAO,gBAAgB,KAAK,MAAM,QAAQ;AAE5C,SAAO;;;;;CAMT,AAAQ,gBACN,SACqC;AACrC,MAAI,CAAC,QAAS,QAAO;EAErB,MAAM,YAAqC,EAAE;AAC7C,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,CAChD,KAAI,KAAK,iBAAiB,IAAI,CAC5B,WAAU,OAAO;WACR,OAAO,UAAU,YAAY,UAAU,KAEhD,WAAU,OAAO,MAAM,QAAQ,MAAM,GACjC,UAAU,MAAM,OAAO,MACvB;MAEJ,WAAU,OAAO;AAGrB,SAAO;;;;;CAMT,AAAQ,iBAAiB,WAA4B;AACnD,SAAO,yBAAyB,MAAM,YAAY,QAAQ,KAAK,UAAU,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AppKitError } from "./base.js";
|
|
2
|
+
|
|
3
|
+
//#region src/errors/identity-expired.d.ts
|
|
4
|
+
/** The downstream service rejected the active caller's credentials. */
|
|
5
|
+
declare class IdentityExpiredError extends AppKitError {
|
|
6
|
+
readonly tokenFingerprint?: string | undefined;
|
|
7
|
+
readonly code = "IDENTITY_EXPIRED";
|
|
8
|
+
readonly statusCode = 401;
|
|
9
|
+
readonly isRetryable = false;
|
|
10
|
+
constructor(tokenFingerprint?: string | undefined);
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { IdentityExpiredError };
|
|
14
|
+
//# sourceMappingURL=identity-expired.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity-expired.d.ts","names":[],"sources":["../../src/errors/identity-expired.ts"],"mappings":";;;;cAGa,oBAAA,SAA6B,WAAA;EAAA,SAKnB,gBAAA;EAAA,SAJZ,IAAA;EAAA,SACA,UAAA;EAAA,SACA,WAAA;cAEY,gBAAA;AAAA"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { AppKitError } from "./base.js";
|
|
2
|
+
|
|
3
|
+
//#region src/errors/identity-expired.ts
|
|
4
|
+
/** The downstream service rejected the active caller's credentials. */
|
|
5
|
+
var IdentityExpiredError = class extends AppKitError {
|
|
6
|
+
code = "IDENTITY_EXPIRED";
|
|
7
|
+
statusCode = 401;
|
|
8
|
+
isRetryable = false;
|
|
9
|
+
constructor(tokenFingerprint) {
|
|
10
|
+
const message = "Caller credentials were rejected or expired. Reauthenticate and retry with a fresh user token.";
|
|
11
|
+
super(message, {
|
|
12
|
+
clientMessage: message,
|
|
13
|
+
context: { fingerprint: tokenFingerprint }
|
|
14
|
+
});
|
|
15
|
+
this.tokenFingerprint = tokenFingerprint;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
/** Recognize structured HTTP failures, including connector cause wrappers. */
|
|
19
|
+
function isUnauthorized(error) {
|
|
20
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21
|
+
let current = error;
|
|
22
|
+
while (current && typeof current === "object" && !seen.has(current)) {
|
|
23
|
+
seen.add(current);
|
|
24
|
+
const value = current;
|
|
25
|
+
if (!(current instanceof AppKitError) && (value.status === 401 || value.statusCode === 401 || value.response?.status === 401)) return true;
|
|
26
|
+
current = value.cause;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
export { IdentityExpiredError, isUnauthorized };
|
|
33
|
+
//# sourceMappingURL=identity-expired.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity-expired.js","names":[],"sources":["../../src/errors/identity-expired.ts"],"sourcesContent":["import { AppKitError } from \"./base\";\n\n/** The downstream service rejected the active caller's credentials. */\nexport class IdentityExpiredError extends AppKitError {\n readonly code = \"IDENTITY_EXPIRED\";\n readonly statusCode = 401;\n readonly isRetryable = false;\n\n constructor(readonly tokenFingerprint?: string) {\n const message =\n \"Caller credentials were rejected or expired. Reauthenticate and retry with a fresh user token.\";\n // Do not retain upstream errors: SDK errors may contain authorization headers.\n super(message, {\n clientMessage: message,\n context: { fingerprint: tokenFingerprint },\n });\n }\n}\n\n/** Recognize structured HTTP failures, including connector cause wrappers. */\nexport function isUnauthorized(error: unknown): boolean {\n const seen = new Set<object>();\n let current = error;\n while (current && typeof current === \"object\" && !seen.has(current)) {\n seen.add(current);\n const value = current as {\n status?: unknown;\n statusCode?: unknown;\n response?: { status?: unknown };\n cause?: unknown;\n };\n if (\n !(current instanceof AppKitError) &&\n (value.status === 401 ||\n value.statusCode === 401 ||\n value.response?.status === 401)\n )\n return true;\n current = value.cause;\n }\n return false;\n}\n"],"mappings":";;;;AAGA,IAAa,uBAAb,cAA0C,YAAY;CACpD,AAAS,OAAO;CAChB,AAAS,aAAa;CACtB,AAAS,cAAc;CAEvB,YAAY,AAAS,kBAA2B;EAC9C,MAAM,UACJ;AAEF,QAAM,SAAS;GACb,eAAe;GACf,SAAS,EAAE,aAAa,kBAAkB;GAC3C,CAAC;EAPiB;;;;AAYvB,SAAgB,eAAe,OAAyB;CACtD,MAAM,uBAAO,IAAI,KAAa;CAC9B,IAAI,UAAU;AACd,QAAO,WAAW,OAAO,YAAY,YAAY,CAAC,KAAK,IAAI,QAAQ,EAAE;AACnE,OAAK,IAAI,QAAQ;EACjB,MAAM,QAAQ;AAMd,MACE,EAAE,mBAAmB,iBACpB,MAAM,WAAW,OAChB,MAAM,eAAe,OACrB,MAAM,UAAU,WAAW,KAE7B,QAAO;AACT,YAAU,MAAM;;AAElB,QAAO"}
|
package/dist/errors/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AppKitError } from "./base.js";
|
|
2
2
|
import { AuthenticationError } from "./authentication.js";
|
|
3
|
+
import { IdentityExpiredError } from "./identity-expired.js";
|
|
3
4
|
import { ConfigurationError } from "./configuration.js";
|
|
4
5
|
import { ConnectionError } from "./connection.js";
|
|
5
6
|
import { DatabaseValidationError } from "./database-validation.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { StreamExecutionSettings } from "./shared/src/execute.js";
|
|
2
2
|
import { ResourceFieldEntry } from "./shared/src/schemas/manifest.js";
|
|
3
|
-
import { BasePluginConfig, IAppRouter, PluginData, ToPlugin } from "./shared/src/plugin.js";
|
|
3
|
+
import { AppKitApi, BasePluginConfig, IAppRouter, PluginData, ScopedPluginMap, ToPlugin, UserScopedApp } from "./shared/src/plugin.js";
|
|
4
4
|
import { CacheConfig } from "./shared/src/cache.js";
|
|
5
5
|
import { isSQLTypeMarker, sql } from "./shared/src/sql/helpers.js";
|
|
6
6
|
import { ApiError } from "./shared/src/workspace-client/errors.js";
|
|
7
7
|
import { createWorkspaceClient } from "./shared/src/workspace-client/factory.js";
|
|
8
8
|
import "./shared/src/index.js";
|
|
9
9
|
import { WorkspaceClient, WorkspaceClientOptions } from "./workspace-client/index.js";
|
|
10
|
+
import { UserContext, isUserContext } from "./context/user-context.js";
|
|
11
|
+
import { ServiceContext } from "./context/service-context.js";
|
|
12
|
+
import { CallerContext, CallerPrincipal, ExecutionContext } from "./context/caller-context.js";
|
|
13
|
+
import { getCallerContext, getCurrentActorId, getCurrentPrincipalKey, getCurrentUserId, getExecutionContext, getUserContext, isInUserContext, runInCallerContext, runInUserContext } from "./context/execution-context.js";
|
|
14
|
+
import "./context/index.js";
|
|
10
15
|
import { DatabaseRegistry } from "./database/contract/registry.js";
|
|
11
16
|
import { CacheManager } from "./cache/index.js";
|
|
12
17
|
import { JobsConnectorConfig } from "./connectors/jobs/types.js";
|
|
@@ -14,14 +19,17 @@ import "./connectors/jobs/index.js";
|
|
|
14
19
|
import { LakebasePoolManager, createLakebasePoolManager } from "./connectors/lakebase/pool-manager.js";
|
|
15
20
|
import { LakebasePool } from "./connectors/lakebase/routing-pool.js";
|
|
16
21
|
import { DatabaseCredential, GenerateDatabaseCredentialRequest, LakebasePoolConfig, RequestedClaims, RequestedClaimsPermissionSet, RequestedResource, createLakebasePool, generateDatabaseCredential, getLakebaseOrmConfig, getLakebasePgConfig, getUsernameWithApiLookup, getWorkspaceClient } from "./connectors/lakebase/index.js";
|
|
17
|
-
import { getExecutionContext } from "./context/execution-context.js";
|
|
18
|
-
import "./context/index.js";
|
|
19
22
|
import { ITelemetry, TelemetryConfig } from "./telemetry/types.js";
|
|
20
23
|
import { Counter, Histogram, SeverityNumber, Span, SpanStatusCode } from "./telemetry/index.js";
|
|
21
24
|
import { createApp } from "./core/appkit.js";
|
|
22
25
|
import "./core/index.js";
|
|
26
|
+
import { ResourcePermission, ResourceType } from "./registry/types.generated.js";
|
|
27
|
+
import { ConfigSchema, PluginManifest, ResourceEntry, ResourceRequirement, ValidationResult } from "./registry/types.js";
|
|
28
|
+
import { getWarehouseId } from "./resources/warehouse.js";
|
|
29
|
+
import "./resources/index.js";
|
|
23
30
|
import { AppKitError } from "./errors/base.js";
|
|
24
31
|
import { AuthenticationError } from "./errors/authentication.js";
|
|
32
|
+
import { IdentityExpiredError } from "./errors/identity-expired.js";
|
|
25
33
|
import { ConfigurationError } from "./errors/configuration.js";
|
|
26
34
|
import { ConnectionError } from "./errors/connection.js";
|
|
27
35
|
import { DatabaseValidationError, DatabaseValidationIssue } from "./errors/database-validation.js";
|
|
@@ -36,8 +44,6 @@ import { toPlugin } from "./plugin/to-plugin.js";
|
|
|
36
44
|
import "./plugin/index.js";
|
|
37
45
|
import { FileAction, FilePolicy, FilePolicyUser, FileResource, PolicyDeniedError, READ_ACTIONS, WRITE_ACTIONS } from "./plugins/files/policy.js";
|
|
38
46
|
import { analytics } from "./plugins/analytics/analytics.js";
|
|
39
|
-
import { ResourcePermission, ResourceType } from "./registry/types.generated.js";
|
|
40
|
-
import { ConfigSchema, PluginManifest, ResourceEntry, ResourceRequirement, ValidationResult } from "./registry/types.js";
|
|
41
47
|
import { defineManifest, getPluginManifest, getResourceRequirements } from "./registry/manifest-loader.js";
|
|
42
48
|
import { ResourceRegistry } from "./registry/resource-registry.js";
|
|
43
49
|
import "./registry/index.js";
|
|
@@ -54,4 +60,4 @@ import "./plugins/ga-exports.generated.js";
|
|
|
54
60
|
import { extractServingEndpoints, findServerFile } from "./type-generator/serving/server-file-extractor.js";
|
|
55
61
|
import { appKitServingTypesPlugin } from "./type-generator/serving/vite-plugin.js";
|
|
56
62
|
import { appKitTypesPlugin } from "./type-generator/vite-plugin.js";
|
|
57
|
-
export { ApiError, AppKitError, AuthenticationError, type BasePluginConfig, type CacheConfig, CacheManager, type ConfigSchema, ConfigurationError, ConnectionError, type Counter, type DatabaseCredential, type DatabaseRegistry, DatabaseValidationError, type DatabaseValidationIssue, type EndpointConfig, ExecutionError, type ExecutionResult, type FileAction, type FilePolicy, type FilePolicyUser, type FileResource, type GenerateDatabaseCredentialRequest, type Histogram, type IAppRouter, type IJobsConfig, type ITelemetry, InitializationError, type JobAPI, type JobConfig, type JobsConnectorConfig, type JobsExport, type LakebasePool, type LakebasePoolConfig, type LakebasePoolManager, Plugin, type PluginData, type PluginManifest, PolicyDeniedError, READ_ACTIONS, type RequestedClaims, RequestedClaimsPermissionSet, type RequestedResource, type ResourceEntry, type ResourceFieldEntry, type ResourcePermission, ResourceRegistry, type ResourceRequirement, ResourceType, ServerError, type ServingEndpointEntry, type ServingEndpointRegistry, type ServingFactory, SeverityNumber, type Span, SpanStatusCode, type StreamExecutionSettings, type TelemetryConfig, type ToPlugin, TunnelError, ValidationError, type ValidationResult, WRITE_ACTIONS, type WorkspaceClient, type WorkspaceClientOptions, analytics, appKitServingTypesPlugin, appKitTypesPlugin, createApp, createLakebasePool, createLakebasePoolManager, createWorkspaceClient, defineManifest, extractServingEndpoints, files, findServerFile, generateDatabaseCredential, genie, getExecutionContext, getLakebaseOrmConfig, getLakebasePgConfig, getPluginManifest, getResourceRequirements, getUsernameWithApiLookup, getWorkspaceClient, isSQLTypeMarker, jobs, lakebase, server, serving, sql, toPlugin };
|
|
63
|
+
export { ApiError, type AppKitApi, AppKitError, AuthenticationError, type BasePluginConfig, type CacheConfig, CacheManager, type CallerContext, type CallerPrincipal, type ConfigSchema, ConfigurationError, ConnectionError, type Counter, type DatabaseCredential, type DatabaseRegistry, DatabaseValidationError, type DatabaseValidationIssue, type EndpointConfig, type ExecutionContext, ExecutionError, type ExecutionResult, type FileAction, type FilePolicy, type FilePolicyUser, type FileResource, type GenerateDatabaseCredentialRequest, type Histogram, type IAppRouter, type IJobsConfig, type ITelemetry, IdentityExpiredError, InitializationError, type JobAPI, type JobConfig, type JobsConnectorConfig, type JobsExport, type LakebasePool, type LakebasePoolConfig, type LakebasePoolManager, Plugin, type PluginData, type PluginManifest, PolicyDeniedError, READ_ACTIONS, type RequestedClaims, RequestedClaimsPermissionSet, type RequestedResource, type ResourceEntry, type ResourceFieldEntry, type ResourcePermission, ResourceRegistry, type ResourceRequirement, ResourceType, type ScopedPluginMap, ServerError, ServiceContext, type ServingEndpointEntry, type ServingEndpointRegistry, type ServingFactory, SeverityNumber, type Span, SpanStatusCode, type StreamExecutionSettings, type TelemetryConfig, type ToPlugin, TunnelError, type UserContext, type UserScopedApp, ValidationError, type ValidationResult, WRITE_ACTIONS, type WorkspaceClient, type WorkspaceClientOptions, analytics, appKitServingTypesPlugin, appKitTypesPlugin, createApp, createLakebasePool, createLakebasePoolManager, createWorkspaceClient, defineManifest, extractServingEndpoints, files, findServerFile, generateDatabaseCredential, genie, getCallerContext, getCurrentActorId, getCurrentPrincipalKey, getCurrentUserId, getExecutionContext, getLakebaseOrmConfig, getLakebasePgConfig, getPluginManifest, getResourceRequirements, getUserContext, getUsernameWithApiLookup, getWarehouseId, getWorkspaceClient, isInUserContext, isSQLTypeMarker, isUserContext, jobs, lakebase, runInCallerContext, runInUserContext, server, serving, sql, toPlugin };
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { ApiError } from "./shared/src/workspace-client/errors.js";
|
|
|
3
3
|
import { createWorkspaceClient } from "./shared/src/workspace-client/factory.js";
|
|
4
4
|
import { AppKitError } from "./errors/base.js";
|
|
5
5
|
import { AuthenticationError } from "./errors/authentication.js";
|
|
6
|
+
import { IdentityExpiredError } from "./errors/identity-expired.js";
|
|
6
7
|
import { ConfigurationError } from "./errors/configuration.js";
|
|
7
8
|
import { ConnectionError } from "./errors/connection.js";
|
|
8
9
|
import { DatabaseValidationError } from "./errors/database-validation.js";
|
|
@@ -12,8 +13,11 @@ import { ServerError } from "./errors/server.js";
|
|
|
12
13
|
import { TunnelError } from "./errors/tunnel.js";
|
|
13
14
|
import { ValidationError } from "./errors/validation.js";
|
|
14
15
|
import "./errors/index.js";
|
|
16
|
+
import { getWarehouseId } from "./resources/warehouse.js";
|
|
17
|
+
import { isUserContext } from "./context/user-context.js";
|
|
18
|
+
import { ServiceContext } from "./context/service-context.js";
|
|
15
19
|
import { createLakebasePoolManager } from "./connectors/lakebase/pool-manager.js";
|
|
16
|
-
import { getExecutionContext } from "./context/execution-context.js";
|
|
20
|
+
import { getCallerContext, getCurrentActorId, getCurrentPrincipalKey, getCurrentUserId, getExecutionContext, getUserContext, isInUserContext, runInCallerContext, runInUserContext } from "./context/execution-context.js";
|
|
17
21
|
import { RequestedClaimsPermissionSet, createLakebasePool, generateDatabaseCredential, getLakebaseOrmConfig, getLakebasePgConfig, getUsernameWithApiLookup, getWorkspaceClient } from "./connectors/lakebase/index.js";
|
|
18
22
|
import { SeverityNumber, SpanStatusCode } from "./telemetry/index.js";
|
|
19
23
|
import { CacheManager } from "./cache/index.js";
|
|
@@ -27,6 +31,7 @@ import { ResourceRegistry } from "./registry/resource-registry.js";
|
|
|
27
31
|
import "./registry/index.js";
|
|
28
32
|
import { createApp } from "./core/appkit.js";
|
|
29
33
|
import "./core/index.js";
|
|
34
|
+
import "./resources/index.js";
|
|
30
35
|
import { PolicyDeniedError, READ_ACTIONS, WRITE_ACTIONS } from "./plugins/files/policy.js";
|
|
31
36
|
import { analytics } from "./plugins/analytics/analytics.js";
|
|
32
37
|
import { files } from "./plugins/files/plugin.js";
|
|
@@ -40,4 +45,4 @@ import { server } from "./plugins/server/index.js";
|
|
|
40
45
|
import { serving } from "./plugins/serving/serving.js";
|
|
41
46
|
import "./plugins/ga-exports.generated.js";
|
|
42
47
|
|
|
43
|
-
export { ApiError, AppKitError, AuthenticationError, CacheManager, ConfigurationError, ConnectionError, DatabaseValidationError, ExecutionError, InitializationError, Plugin, PolicyDeniedError, READ_ACTIONS, RequestedClaimsPermissionSet, ResourceRegistry, ResourceType, ServerError, SeverityNumber, SpanStatusCode, TunnelError, ValidationError, WRITE_ACTIONS, analytics, appKitServingTypesPlugin, appKitTypesPlugin, createApp, createLakebasePool, createLakebasePoolManager, createWorkspaceClient, defineManifest, extractServingEndpoints, files, findServerFile, generateDatabaseCredential, genie, getExecutionContext, getLakebaseOrmConfig, getLakebasePgConfig, getPluginManifest, getResourceRequirements, getUsernameWithApiLookup, getWorkspaceClient, isSQLTypeMarker, jobs, lakebase, server, serving, sql, toPlugin };
|
|
48
|
+
export { ApiError, AppKitError, AuthenticationError, CacheManager, ConfigurationError, ConnectionError, DatabaseValidationError, ExecutionError, IdentityExpiredError, InitializationError, Plugin, PolicyDeniedError, READ_ACTIONS, RequestedClaimsPermissionSet, ResourceRegistry, ResourceType, ServerError, ServiceContext, SeverityNumber, SpanStatusCode, TunnelError, ValidationError, WRITE_ACTIONS, analytics, appKitServingTypesPlugin, appKitTypesPlugin, createApp, createLakebasePool, createLakebasePoolManager, createWorkspaceClient, defineManifest, extractServingEndpoints, files, findServerFile, generateDatabaseCredential, genie, getCallerContext, getCurrentActorId, getCurrentPrincipalKey, getCurrentUserId, getExecutionContext, getLakebaseOrmConfig, getLakebasePgConfig, getPluginManifest, getResourceRequirements, getUserContext, getUsernameWithApiLookup, getWarehouseId, getWorkspaceClient, isInUserContext, isSQLTypeMarker, isUserContext, jobs, lakebase, runInCallerContext, runInUserContext, server, serving, sql, toPlugin };
|
package/dist/logging/logger.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { DEFAULT_SAMPLING_CONFIG, shouldSample } from "./sampling.js";
|
|
2
2
|
import { WideEvent } from "./wide-event.js";
|
|
3
3
|
import { WideEventEmitter } from "./wide-event-emitter.js";
|
|
4
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
4
|
import { format } from "node:util";
|
|
5
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { trace } from "@opentelemetry/api";
|
|
7
7
|
import { createDebug } from "obug";
|
|
8
8
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { IdentityExpiredError } from "../errors/identity-expired.js";
|
|
2
|
+
|
|
1
3
|
//#region src/plugin/execution-result.d.ts
|
|
2
4
|
/**
|
|
3
5
|
* Discriminated union for plugin execution results.
|
|
@@ -19,7 +21,8 @@ type ExecutionResult<T> = {
|
|
|
19
21
|
} | {
|
|
20
22
|
ok: false;
|
|
21
23
|
status: number;
|
|
22
|
-
message: string;
|
|
24
|
+
message: string; /** Typed credential expiry without changing the existing failure envelope. */
|
|
25
|
+
error?: IdentityExpiredError;
|
|
23
26
|
};
|
|
24
27
|
//#endregion
|
|
25
28
|
export { ExecutionResult };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execution-result.d.ts","names":[],"sources":["../../src/plugin/execution-result.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"execution-result.d.ts","names":[],"sources":["../../src/plugin/execution-result.ts"],"mappings":";;;;;AAgBA;;;;;;;;;;;;KAAY,eAAA;EACN,EAAA;EAAU,IAAA,EAAM,CAAA;AAAA;EAEhB,EAAA;EACA,MAAA;EACA,OAAA;EAEA,KAAA,GAAQ,oBAAA;AAAA"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getCurrentPrincipalId, isInUserContext, normalizeIdentityError } from "../../context/execution-context.js";
|
|
2
2
|
import { SpanStatusCode } from "../../telemetry/index.js";
|
|
3
|
-
import { isDevOboFallback } from "
|
|
3
|
+
import { isDevOboFallback } from "../../context/request-scope.js";
|
|
4
|
+
import "../plugin.js";
|
|
4
5
|
|
|
5
6
|
//#region src/plugin/interceptors/telemetry.ts
|
|
6
7
|
var TelemetryInterceptor = class {
|
|
@@ -29,12 +30,13 @@ var TelemetryInterceptor = class {
|
|
|
29
30
|
}
|
|
30
31
|
try {
|
|
31
32
|
span.setAttribute("execution.context", isInUserContext() ? "user" : "service");
|
|
32
|
-
span.setAttribute("caller.id",
|
|
33
|
+
span.setAttribute("caller.id", getCurrentPrincipalId());
|
|
33
34
|
if (isDevOboFallback()) span.setAttribute("execution.obo_dev_fallback", true);
|
|
34
35
|
const result = await fn();
|
|
35
36
|
if (!isAborted) span.setStatus({ code: SpanStatusCode.OK });
|
|
36
37
|
return result;
|
|
37
|
-
} catch (
|
|
38
|
+
} catch (caught) {
|
|
39
|
+
const error = normalizeIdentityError(caught);
|
|
38
40
|
if (!isAborted) {
|
|
39
41
|
span.recordException(error);
|
|
40
42
|
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry.js","names":[],"sources":["../../../src/plugin/interceptors/telemetry.ts"],"sourcesContent":["import type { TelemetryConfig } from \"shared\";\n\nimport {\n
|
|
1
|
+
{"version":3,"file":"telemetry.js","names":[],"sources":["../../../src/plugin/interceptors/telemetry.ts"],"sourcesContent":["import type { TelemetryConfig } from \"shared\";\n\nimport {\n getCurrentPrincipalId,\n isInUserContext,\n normalizeIdentityError,\n} from \"../../context/execution-context\";\nimport type { ITelemetry, Span } from \"../../telemetry\";\nimport { SpanStatusCode } from \"../../telemetry\";\nimport { isDevOboFallback } from \"../plugin\";\nimport type { ExecutionInterceptor, InterceptorContext } from \"./types\";\n\nexport class TelemetryInterceptor implements ExecutionInterceptor {\n constructor(\n private telemetry: ITelemetry,\n private config?: TelemetryConfig,\n ) {}\n\n async intercept<T>(\n fn: () => Promise<T>,\n context: InterceptorContext,\n ): Promise<T> {\n const spanName = this.config?.spanName || \"plugin.execute\";\n\n // abort operation if signal is aborted\n if (context.signal?.aborted) {\n throw new Error(\"Operation aborted before execution\");\n }\n\n return this.telemetry.startActiveSpan(\n spanName,\n { attributes: this.config?.attributes },\n async (span: Span) => {\n let abortHandler: (() => void) | undefined;\n let isAborted = false;\n\n if (context.signal) {\n abortHandler = () => {\n // abort span if not recording\n if (!span.isRecording()) return;\n isAborted = true;\n span.setAttribute(\"cancelled\", true);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: \"Operation cancelled by client\",\n });\n span.end();\n };\n context.signal.addEventListener(\"abort\", abortHandler, {\n once: true,\n });\n }\n\n try {\n span.setAttribute(\n \"execution.context\",\n isInUserContext() ? \"user\" : \"service\",\n );\n span.setAttribute(\"caller.id\", getCurrentPrincipalId());\n if (isDevOboFallback()) {\n span.setAttribute(\"execution.obo_dev_fallback\", true);\n }\n\n const result = await fn();\n if (!isAborted) {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n return result;\n } catch (caught) {\n const error = normalizeIdentityError(caught);\n if (!isAborted) {\n span.recordException(error as Error);\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n throw error;\n } finally {\n if (abortHandler && context.signal) {\n context.signal.removeEventListener(\"abort\", abortHandler);\n }\n if (!isAborted) {\n span.end();\n }\n }\n },\n );\n }\n}\n"],"mappings":";;;;;;AAYA,IAAa,uBAAb,MAAkE;CAChE,YACE,AAAQ,WACR,AAAQ,QACR;EAFQ;EACA;;CAGV,MAAM,UACJ,IACA,SACY;EACZ,MAAM,WAAW,KAAK,QAAQ,YAAY;AAG1C,MAAI,QAAQ,QAAQ,QAClB,OAAM,IAAI,MAAM,qCAAqC;AAGvD,SAAO,KAAK,UAAU,gBACpB,UACA,EAAE,YAAY,KAAK,QAAQ,YAAY,EACvC,OAAO,SAAe;GACpB,IAAI;GACJ,IAAI,YAAY;AAEhB,OAAI,QAAQ,QAAQ;AAClB,yBAAqB;AAEnB,SAAI,CAAC,KAAK,aAAa,CAAE;AACzB,iBAAY;AACZ,UAAK,aAAa,aAAa,KAAK;AACpC,UAAK,UAAU;MACb,MAAM,eAAe;MACrB,SAAS;MACV,CAAC;AACF,UAAK,KAAK;;AAEZ,YAAQ,OAAO,iBAAiB,SAAS,cAAc,EACrD,MAAM,MACP,CAAC;;AAGJ,OAAI;AACF,SAAK,aACH,qBACA,iBAAiB,GAAG,SAAS,UAC9B;AACD,SAAK,aAAa,aAAa,uBAAuB,CAAC;AACvD,QAAI,kBAAkB,CACpB,MAAK,aAAa,8BAA8B,KAAK;IAGvD,MAAM,SAAS,MAAM,IAAI;AACzB,QAAI,CAAC,UACH,MAAK,UAAU,EAAE,MAAM,eAAe,IAAI,CAAC;AAE7C,WAAO;YACA,QAAQ;IACf,MAAM,QAAQ,uBAAuB,OAAO;AAC5C,QAAI,CAAC,WAAW;AACd,UAAK,gBAAgB,MAAe;AACpC,UAAK,UAAU,EAAE,MAAM,eAAe,OAAO,CAAC;;AAEhD,UAAM;aACE;AACR,QAAI,gBAAgB,QAAQ,OAC1B,SAAQ,OAAO,oBAAoB,SAAS,aAAa;AAE3D,QAAI,CAAC,UACH,MAAK,KAAK;;IAIjB"}
|
package/dist/plugin/plugin.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { PluginContext } from "../core/plugin-context.js";
|
|
|
10
10
|
import { StreamManager } from "../stream/stream-manager.js";
|
|
11
11
|
import "../stream/index.js";
|
|
12
12
|
import { DevFileReader } from "./dev-reader.js";
|
|
13
|
+
import "../context/request-scope.js";
|
|
13
14
|
import express from "express";
|
|
14
15
|
|
|
15
16
|
//#region src/plugin/plugin.d.ts
|
|
@@ -215,37 +216,27 @@ declare abstract class Plugin<TConfig extends BasePluginConfig = BasePluginConfi
|
|
|
215
216
|
*
|
|
216
217
|
* Returns the `x-forwarded-user` header when present. In development mode
|
|
217
218
|
* (`NODE_ENV=development`) falls back to the current context user ID so
|
|
218
|
-
* that callers outside an active `
|
|
219
|
+
* that callers outside an active `runInCallerContext` scope still get a
|
|
219
220
|
* consistent value.
|
|
220
221
|
*
|
|
221
222
|
* @throws AuthenticationError in production when no user header is present.
|
|
222
223
|
*/
|
|
223
224
|
protected resolveUserId(req: express.Request): string;
|
|
224
|
-
/**
|
|
225
|
-
* Execute operations using the user's identity from the request.
|
|
226
|
-
* Returns a proxy of this plugin where all method calls execute
|
|
227
|
-
* with the user's Databricks credentials instead of the service principal.
|
|
228
|
-
*
|
|
229
|
-
* @param req - The Express request containing the user token in headers
|
|
230
|
-
* @returns A proxied plugin instance that executes as the user
|
|
231
|
-
* @throws AuthenticationError if user token is not available in request headers (production only).
|
|
232
|
-
* In development mode (`NODE_ENV=development`), skips user impersonation instead of throwing.
|
|
233
|
-
*/
|
|
225
|
+
/** @deprecated Use appkit.asUser(req) to scope the whole app. */
|
|
234
226
|
asUser(req: express.Request): this;
|
|
235
227
|
/**
|
|
236
|
-
*
|
|
237
|
-
*
|
|
228
|
+
* User-scoped executor for the plugin's own on-behalf-of routing (for
|
|
229
|
+
* example `.obo.sql` lanes), using the same caller context as the
|
|
230
|
+
* app-level `appkit.asUser(req)`.
|
|
238
231
|
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
232
|
+
* Unlike the deprecated public `asUser(req)`, this does not emit a
|
|
233
|
+
* deprecation warning and does not opt out of the app-only resource
|
|
234
|
+
* guards, so internal routing matches current on-behalf-of semantics
|
|
235
|
+
* rather than the retained legacy behavior.
|
|
242
236
|
*
|
|
243
|
-
*
|
|
244
|
-
* exports object never re-enter the proxy's `get` trap. Wrapping them
|
|
245
|
-
* here is the only way to make the user context follow function
|
|
246
|
-
* references back out of the plugin.
|
|
237
|
+
* @internal
|
|
247
238
|
*/
|
|
248
|
-
|
|
239
|
+
protected _asUserScoped(req: express.Request): this;
|
|
249
240
|
protected executeStream<T>(res: IAppResponse, fn: StreamExecuteHandler<T>, options: StreamExecutionSettings, userKey?: string): Promise<void>;
|
|
250
241
|
/**
|
|
251
242
|
* Execute a function with the plugin's interceptor chain.
|
|
@@ -254,7 +245,8 @@ declare abstract class Plugin<TConfig extends BasePluginConfig = BasePluginConfi
|
|
|
254
245
|
* - `{ ok: true, data: T }` on success
|
|
255
246
|
* - `{ ok: false, status: number, message: string }` on failure
|
|
256
247
|
*
|
|
257
|
-
*
|
|
248
|
+
* Caller credential expiration retains the failure result and additionally
|
|
249
|
+
* exposes a typed error, preserving existing result-based callers.
|
|
258
250
|
*/
|
|
259
251
|
protected execute<T>(fn: (signal?: AbortSignal) => Promise<T>, options: PluginExecutionSettings, userKey?: string): Promise<ExecutionResult<T>>;
|
|
260
252
|
protected registerEndpoint(name: string, path: string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","names":[],"sources":["../../src/plugin/plugin.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","names":[],"sources":["../../src/plugin/plugin.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwJA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAAsB,MAAA,iBACJ,gBAAA,GAAmB,gBAAA,aACxB,UAAA;EAAA,UA4BW,MAAA,EAAQ,OAAA;EAAA,UA3BpB,OAAA;EAAA,UACA,KAAA,EAAQ,YAAA;EAAA,UACR,GAAA,EAAK,UAAA;EAAA,UACL,aAAA,EAAe,aAAA;EAAA,UACf,aAAA,EAAe,aAAA;EAAA,UACf,SAAA,EAAY,UAAA;EAAA,UACZ,OAAA,GAAU,aAAA;EA0LV;EAAA,QAvLF,mBAAA;EAuL6B;EAAA,QApL7B,oBAAA;EA4LR;;;;;;EAAA,OApLO,KAAA,EAAO,WAAA;EAyMU;;;EApMxB,IAAA;cAEsB,MAAA,EAAQ,OAAA;EAAA,QAoBtB,gBAAA;EAyLmB;;;;;;;;EApK3B,aAAA,CACE,IAAA;IACE,OAAA;IACA,eAAA,GAAkB,gBAAA;EAAA;EAgBtB,YAAA,CAAa,CAAA,EAAG,OAAA,CAAQ,MAAA;EAIlB,KAAA,CAAA,GAAK,OAAA;EAEX,YAAA,CAAA,GAAgB,iBAAA;EAIhB,uBAAA,CAAA,GAA2B,WAAA;EAI3B,qBAAA,CAAA;EA+NW;;;;;;;;;;;;;;;;;;;;;;;;EAnMX,OAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiDA,YAAA,CAAA,GAAgB,MAAA;;;;;;;;;;;YAcN,aAAA,CAAc,GAAA,EAAK,OAAA,CAAQ,OAAA;;EAQrC,MAAA,CAAO,GAAA,EAAK,OAAA,CAAQ,OAAA;;;;;;;;;;;;;YAqBV,aAAA,CAAc,GAAA,EAAK,OAAA,CAAQ,OAAA;EAAA,UASrB,aAAA,GAAA,CACd,GAAA,EAAK,YAAA,EACL,EAAA,EAAI,oBAAA,CAAqB,CAAA,GACzB,OAAA,EAAS,uBAAA,EACT,OAAA,YAAgB,OAAA;;;;;;;;;;;YAsFF,OAAA,GAAA,CACd,EAAA,GAAK,MAAA,GAAS,WAAA,KAAgB,OAAA,CAAQ,CAAA,GACtC,OAAA,EAAS,uBAAA,EACT,OAAA,YACC,OAAA,CAAQ,eAAA,CAAgB,CAAA;EAAA,UAsDjB,gBAAA,CAAiB,IAAA,UAAc,IAAA;EAAA,UAI/B,KAAA,YAAA,CACR,MAAA,EAAQ,OAAA,CAAQ,MAAA,EAChB,MAAA,EAAQ,WAAA;EAAA,QAeF,qBAAA;EAAA,QAaA,kBAAA;EAAA,QAqCM,wBAAA;EAAA,QAqBN,iBAAA;AAAA"}
|