@agent-native/core 0.200.0-nightly-20261002124406 → 0.200.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/dist/agent/production-agent.d.ts +5 -1
- package/dist/agent/production-agent.js +36 -1
- package/dist/app-config/runtime.d.ts +1 -0
- package/dist/app-config/runtime.js +4 -0
- package/dist/app-config/schema.d.ts +1 -0
- package/dist/cli/design-connect.d.ts +1 -0
- package/dist/cli/design-connect.js +147 -7
- package/dist/client/analytics.js +24 -1
- package/dist/client/session-replay.d.ts +6 -0
- package/dist/client/session-replay.js +47 -5
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/builder.js +20 -2
- package/dist/mcp-client/app-api.d.ts +11 -1
- package/dist/mcp-client/app-api.js +17 -3
- package/dist/mcp-client/index.d.ts +1 -1
- package/dist/mcp-client/manager.d.ts +3 -1
- package/dist/mcp-client/manager.js +4 -0
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/metrics.js +14 -7
- package/dist/provider-api/actions/custom-provider-registration.d.ts +6 -6
- package/dist/provider-api/actions/provider-api.d.ts +15 -15
- package/dist/resource-changes/store.d.ts +166 -0
- package/dist/resource-changes/store.js +499 -0
- package/dist/search/index-store.d.ts +9 -0
- package/dist/search/index-store.js +50 -0
- package/dist/search/index.d.ts +5 -1
- package/dist/search/index.js +5 -0
- package/dist/search/indexer.d.ts +32 -0
- package/dist/search/indexer.js +651 -0
- package/dist/search/query-parser.d.ts +29 -0
- package/dist/search/query-parser.js +71 -0
- package/dist/search/query.d.ts +46 -0
- package/dist/search/query.js +149 -0
- package/dist/search/registry.d.ts +55 -0
- package/dist/search/registry.js +93 -0
- package/dist/search/tokenize.d.ts +103 -0
- package/dist/search/tokenize.js +367 -0
- package/dist/server/action-change-marker-write.js +5 -1
- package/dist/server/agent-chat/run-code-tools.d.ts +7 -0
- package/dist/server/agent-chat/run-code-tools.js +7 -0
- package/dist/server/agent-chat-plugin.d.ts +5 -1
- package/dist/server/agent-chat-plugin.js +18 -12
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +2 -2
- package/dist/server/release-schema.js +8 -0
- package/dist/triggers/actions/manage-automation.d.ts +3 -3
- package/package.json +6 -9
|
@@ -10,10 +10,20 @@ export interface AppMcpTool {
|
|
|
10
10
|
}
|
|
11
11
|
export interface ListVisibleMcpToolsOptions {
|
|
12
12
|
serverId?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Keep only tools served from this provider's MCP endpoint, matched on the
|
|
15
|
+
* server URL. A server id is a name the user chose, so it cannot prove which
|
|
16
|
+
* provider answers the call.
|
|
17
|
+
*/
|
|
18
|
+
providerId?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface CallMcpToolOptions {
|
|
21
|
+
/** Refuse the call unless the server's URL belongs to this provider. */
|
|
22
|
+
providerId?: string;
|
|
13
23
|
}
|
|
14
24
|
export declare class McpAppApiError extends Error {
|
|
15
25
|
readonly statusCode: 401 | 403 | 503;
|
|
16
26
|
constructor(message: string, statusCode: 401 | 403 | 503);
|
|
17
27
|
}
|
|
18
28
|
export declare function listVisibleMcpTools(options?: ListVisibleMcpToolsOptions): Promise<AppMcpTool[]>;
|
|
19
|
-
export declare function callMcpTool(serverId: string, originalToolName: string, args?: Record<string, unknown
|
|
29
|
+
export declare function callMcpTool(serverId: string, originalToolName: string, args?: Record<string, unknown>, options?: CallMcpToolOptions): Promise<unknown>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isToolVisibilityModelOnly } from "@modelcontextprotocol/ext-apps/app-bridge";
|
|
2
2
|
import { getMcpManagerForPrincipal } from "../server/agent-chat/mcp-glue.js";
|
|
3
3
|
import { getRequestContext } from "../server/request-context.js";
|
|
4
|
+
import { hasMcpProviderMatchRules, mcpServerUrlMatchesProvider, } from "../shared/mcp-provider-hosts.js";
|
|
4
5
|
import { buildMcpToolName, } from "./manager.js";
|
|
5
6
|
import { normalizeMcpPrincipal } from "./principal.js";
|
|
6
7
|
import { parseMergedKey } from "./remote-store.js";
|
|
@@ -20,16 +21,19 @@ export async function listVisibleMcpTools(options = {}) {
|
|
|
20
21
|
? manager.getToolsForServer(options.serverId)
|
|
21
22
|
: manager.getTools();
|
|
22
23
|
return tools
|
|
23
|
-
.filter((tool) => isToolVisibleToApp(tool, context)
|
|
24
|
+
.filter((tool) => isToolVisibleToApp(tool, context) &&
|
|
25
|
+
isServerForProvider(manager, tool.source, options.providerId))
|
|
24
26
|
.map(toAppMcpTool);
|
|
25
27
|
}
|
|
26
|
-
export async function callMcpTool(serverId, originalToolName, args = {}) {
|
|
28
|
+
export async function callMcpTool(serverId, originalToolName, args = {}, options = {}) {
|
|
27
29
|
const context = requireAuthenticatedRequest();
|
|
28
30
|
const manager = await requireMcpManager(context);
|
|
29
31
|
const tool = manager
|
|
30
32
|
.getToolsForServer(serverId)
|
|
31
33
|
.find((candidate) => candidate.originalName === originalToolName);
|
|
32
|
-
if (!tool ||
|
|
34
|
+
if (!tool ||
|
|
35
|
+
!isToolVisibleToApp(tool, context) ||
|
|
36
|
+
!isServerForProvider(manager, serverId, options.providerId)) {
|
|
33
37
|
throw new McpAppApiError("MCP tool is not available in this request scope.", 403);
|
|
34
38
|
}
|
|
35
39
|
return manager.callTool(buildMcpToolName(serverId, originalToolName), args);
|
|
@@ -81,6 +85,16 @@ function isToolVisibleToApp(tool, context) {
|
|
|
81
85
|
return false;
|
|
82
86
|
}
|
|
83
87
|
}
|
|
88
|
+
function isServerForProvider(manager, serverId, providerId) {
|
|
89
|
+
if (providerId === undefined)
|
|
90
|
+
return true;
|
|
91
|
+
if (!hasMcpProviderMatchRules(providerId)) {
|
|
92
|
+
throw new Error(`No MCP provider match rules for "${providerId}". Add it to MCP_PROVIDER_ENDPOINTS or MCP_LINK_HOSTS before filtering by it.`);
|
|
93
|
+
}
|
|
94
|
+
const config = manager.getServerConfig(serverId);
|
|
95
|
+
return (config?.type === "http" &&
|
|
96
|
+
mcpServerUrlMatchesProvider(providerId, config.url) === true);
|
|
97
|
+
}
|
|
84
98
|
function toAppMcpTool(tool) {
|
|
85
99
|
return {
|
|
86
100
|
serverId: tool.source,
|
|
@@ -10,7 +10,7 @@ export { mountMcpServersRoutes, buildMergedConfig, builtinMergedConfigKey, McpCo
|
|
|
10
10
|
export { mountMcpHubRoutes, listHubServers, getHubStatus, isHubServeEnabled, isHubConsumeEnabled, type HubServerRecord, type HubServersResponse, } from "./hub-routes.js";
|
|
11
11
|
export { fetchHubServers } from "./hub-client.js";
|
|
12
12
|
export { isMcpToolAllowedForRequest } from "./visibility.js";
|
|
13
|
-
export { callMcpTool, listVisibleMcpTools, McpAppApiError, type AppMcpTool, type ListVisibleMcpToolsOptions, } from "./app-api.js";
|
|
13
|
+
export { callMcpTool, listVisibleMcpTools, McpAppApiError, type AppMcpTool, type CallMcpToolOptions, type ListVisibleMcpToolsOptions, } from "./app-api.js";
|
|
14
14
|
export { classifyMcpToolCall, evaluateMcpToolCallPolicy, type McpToolCallClassification, type McpToolEffect, type McpToolFamily, type McpToolInvocationPolicy, type McpToolPolicyDecision, } from "./tool-policy.js";
|
|
15
15
|
export { configureScreenMemory, queryScreenMemoryForAgent, queryScreenMemoryContext, readScreenMemoryStatus, type ScreenMemoryConfig, type ScreenMemoryAgentQueryResult, type ScreenMemoryContextItem, type ScreenMemoryCoverageGap, type ScreenMemoryEvidenceItem, type ScreenMemoryEvidenceSourceType, type ScreenMemoryQueryResult, type ScreenMemoryRetrievalCoverage, type ScreenMemorySegmentReference, type ScreenMemoryStatus, type ScreenMemoryTimeRange, type ScreenMemoryTruncation, } from "./screen-memory-local.js";
|
|
16
16
|
export { MCP_ACTION_RESULT_MARKER, isMcpActionResult, type AgentMcpAppPayload, type AgentMcpAppResourceContent, type McpActionResult, } from "./app-result.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { McpConfig } from "./config.js";
|
|
1
|
+
import type { McpConfig, McpServerConfig } from "./config.js";
|
|
2
2
|
export declare const MCP_TOOL_PREFIX = "mcp__";
|
|
3
3
|
export interface McpTool {
|
|
4
4
|
source: string;
|
|
@@ -66,6 +66,8 @@ export declare class McpClientManager {
|
|
|
66
66
|
getTools(): McpTool[];
|
|
67
67
|
getTool(prefixedName: string): McpTool | null;
|
|
68
68
|
getToolsForServer(serverId: string): McpTool[];
|
|
69
|
+
/** The config the server's current tools were loaded from. */
|
|
70
|
+
getServerConfig(serverId: string): McpServerConfig | null;
|
|
69
71
|
hasServer(serverId: string): boolean;
|
|
70
72
|
callTool(prefixedName: string, args: unknown): Promise<unknown>;
|
|
71
73
|
readResource(serverId: string, uri: string): Promise<unknown>;
|
|
@@ -526,6 +526,10 @@ export class McpClientManager {
|
|
|
526
526
|
getToolsForServer(serverId) {
|
|
527
527
|
return [...(this.servers.get(serverId)?.tools ?? [])];
|
|
528
528
|
}
|
|
529
|
+
/** The config the server's current tools were loaded from. */
|
|
530
|
+
getServerConfig(serverId) {
|
|
531
|
+
return this.servers.get(serverId)?.config ?? null;
|
|
532
|
+
}
|
|
529
533
|
hasServer(serverId) {
|
|
530
534
|
const entry = this.servers.get(serverId);
|
|
531
535
|
return !!entry?.client && !entry.error;
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
|
|
2
|
+
error?: undefined;
|
|
2
3
|
count: number;
|
|
3
4
|
updated?: undefined;
|
|
4
5
|
ok?: undefined;
|
|
5
|
-
error?: undefined;
|
|
6
6
|
} | {
|
|
7
|
+
error?: undefined;
|
|
7
8
|
count?: undefined;
|
|
8
9
|
updated: number;
|
|
9
10
|
ok?: undefined;
|
|
10
|
-
error?: undefined;
|
|
11
11
|
} | {
|
|
12
12
|
count?: undefined;
|
|
13
13
|
updated?: undefined;
|
|
14
14
|
error: string;
|
|
15
15
|
ok?: undefined;
|
|
16
16
|
} | {
|
|
17
|
+
error?: undefined;
|
|
17
18
|
count?: undefined;
|
|
18
19
|
updated?: undefined;
|
|
19
20
|
ok: boolean;
|
|
20
|
-
error?: undefined;
|
|
21
21
|
}>>;
|
|
@@ -64,8 +64,11 @@ export function recordHttpServerRequest(request) {
|
|
|
64
64
|
function flushErrorType(error) {
|
|
65
65
|
return error instanceof Error && error.name ? error.name : "unknown";
|
|
66
66
|
}
|
|
67
|
-
function recordFlushFailure(errorType) {
|
|
68
|
-
instruments()?.flushFailures.add(1, {
|
|
67
|
+
function recordFlushFailure(signal, errorType) {
|
|
68
|
+
instruments()?.flushFailures.add(1, {
|
|
69
|
+
"agent_native.telemetry.signal": signal,
|
|
70
|
+
"error.type": errorType,
|
|
71
|
+
});
|
|
69
72
|
}
|
|
70
73
|
/**
|
|
71
74
|
* Export buffered telemetry before a serverless function can freeze. Never
|
|
@@ -85,17 +88,21 @@ export async function flushObservability() {
|
|
|
85
88
|
try {
|
|
86
89
|
// One provider failing must not end the wait for the other: the response
|
|
87
90
|
// hook returning early lets the runtime freeze mid-export.
|
|
88
|
-
const
|
|
91
|
+
const flushes = [
|
|
92
|
+
["metrics", provider.meterProvider],
|
|
93
|
+
["traces", provider.tracerProvider],
|
|
94
|
+
];
|
|
95
|
+
const failures = await Promise.all(flushes.map(([, signalProvider]) => Promise.race([
|
|
89
96
|
(async () => {
|
|
90
|
-
await
|
|
97
|
+
await signalProvider?.forceFlush?.();
|
|
91
98
|
return undefined;
|
|
92
99
|
})().catch(flushErrorType),
|
|
93
100
|
timeout,
|
|
94
101
|
])));
|
|
95
|
-
|
|
102
|
+
failures.forEach((failure, index) => {
|
|
96
103
|
if (failure)
|
|
97
|
-
recordFlushFailure(failure);
|
|
98
|
-
}
|
|
104
|
+
recordFlushFailure(flushes[index][0], failure);
|
|
105
|
+
});
|
|
99
106
|
}
|
|
100
107
|
finally {
|
|
101
108
|
clearTimeout(timer);
|
|
@@ -75,10 +75,11 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
75
75
|
user: "user";
|
|
76
76
|
}>>;
|
|
77
77
|
}, z.core.$strip>>, {
|
|
78
|
+
provider?: undefined;
|
|
78
79
|
id?: undefined;
|
|
79
80
|
deleted?: undefined;
|
|
80
|
-
provider?: undefined;
|
|
81
81
|
message?: undefined;
|
|
82
|
+
found?: undefined;
|
|
82
83
|
providers: {
|
|
83
84
|
id: string;
|
|
84
85
|
label: string;
|
|
@@ -89,7 +90,6 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
89
90
|
updatedAt: number;
|
|
90
91
|
}[];
|
|
91
92
|
count: number;
|
|
92
|
-
found?: undefined;
|
|
93
93
|
registered?: undefined;
|
|
94
94
|
label?: undefined;
|
|
95
95
|
} | {
|
|
@@ -103,8 +103,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
103
103
|
registered?: undefined;
|
|
104
104
|
label?: undefined;
|
|
105
105
|
} | {
|
|
106
|
-
deleted?: undefined;
|
|
107
106
|
provider?: undefined;
|
|
107
|
+
deleted?: undefined;
|
|
108
108
|
message?: undefined;
|
|
109
109
|
providers?: undefined;
|
|
110
110
|
count?: undefined;
|
|
@@ -115,19 +115,19 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
115
115
|
} | {
|
|
116
116
|
provider?: undefined;
|
|
117
117
|
message?: undefined;
|
|
118
|
+
found?: undefined;
|
|
118
119
|
providers?: undefined;
|
|
119
120
|
count?: undefined;
|
|
120
|
-
found?: undefined;
|
|
121
121
|
deleted: boolean;
|
|
122
122
|
id: string;
|
|
123
123
|
registered?: undefined;
|
|
124
124
|
label?: undefined;
|
|
125
125
|
} | {
|
|
126
|
-
deleted?: undefined;
|
|
127
126
|
provider?: undefined;
|
|
127
|
+
deleted?: undefined;
|
|
128
|
+
found?: undefined;
|
|
128
129
|
providers?: undefined;
|
|
129
130
|
count?: undefined;
|
|
130
|
-
found?: undefined;
|
|
131
131
|
registered: boolean;
|
|
132
132
|
id: string;
|
|
133
133
|
label: string;
|
|
@@ -311,10 +311,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
311
311
|
notes?: string | undefined;
|
|
312
312
|
scope?: "org" | "user" | undefined;
|
|
313
313
|
}, {
|
|
314
|
-
message?: undefined;
|
|
315
|
-
provider?: undefined;
|
|
316
314
|
id?: undefined;
|
|
317
|
-
|
|
315
|
+
message?: undefined;
|
|
316
|
+
found?: undefined;
|
|
318
317
|
providers: {
|
|
319
318
|
id: string;
|
|
320
319
|
label: string;
|
|
@@ -325,45 +324,46 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
325
324
|
updatedAt: number;
|
|
326
325
|
}[];
|
|
327
326
|
count: number;
|
|
328
|
-
|
|
327
|
+
provider?: undefined;
|
|
328
|
+
deleted?: undefined;
|
|
329
329
|
registered?: undefined;
|
|
330
330
|
label?: undefined;
|
|
331
331
|
} | {
|
|
332
|
-
message?: undefined;
|
|
333
332
|
id?: undefined;
|
|
334
333
|
count?: undefined;
|
|
335
|
-
|
|
334
|
+
message?: undefined;
|
|
336
335
|
providers?: undefined;
|
|
337
336
|
found: boolean;
|
|
338
337
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
338
|
+
deleted?: undefined;
|
|
339
339
|
registered?: undefined;
|
|
340
340
|
label?: undefined;
|
|
341
341
|
} | {
|
|
342
|
-
message?: undefined;
|
|
343
|
-
provider?: undefined;
|
|
344
342
|
count?: undefined;
|
|
345
|
-
|
|
343
|
+
message?: undefined;
|
|
346
344
|
providers?: undefined;
|
|
345
|
+
provider?: undefined;
|
|
347
346
|
found: boolean;
|
|
348
347
|
id: string;
|
|
348
|
+
deleted?: undefined;
|
|
349
349
|
registered?: undefined;
|
|
350
350
|
label?: undefined;
|
|
351
351
|
} | {
|
|
352
|
-
message?: undefined;
|
|
353
|
-
provider?: undefined;
|
|
354
352
|
count?: undefined;
|
|
355
|
-
|
|
353
|
+
message?: undefined;
|
|
356
354
|
found?: undefined;
|
|
355
|
+
providers?: undefined;
|
|
356
|
+
provider?: undefined;
|
|
357
357
|
deleted: boolean;
|
|
358
358
|
id: string;
|
|
359
359
|
registered?: undefined;
|
|
360
360
|
label?: undefined;
|
|
361
361
|
} | {
|
|
362
|
-
provider?: undefined;
|
|
363
362
|
count?: undefined;
|
|
364
|
-
deleted?: undefined;
|
|
365
|
-
providers?: undefined;
|
|
366
363
|
found?: undefined;
|
|
364
|
+
providers?: undefined;
|
|
365
|
+
provider?: undefined;
|
|
366
|
+
deleted?: undefined;
|
|
367
367
|
registered: boolean;
|
|
368
368
|
id: string;
|
|
369
369
|
label: string;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app resource change feed: a durable note that one of an app's
|
|
3
|
+
* resources changed, kept once for every consumer that asked to hear about
|
|
4
|
+
* that resource type.
|
|
5
|
+
*
|
|
6
|
+
* Producers call one SQL function, `agent_native_app_resource_changed`, in the
|
|
7
|
+
* writer's own transaction, so nothing is recorded unless the write commits.
|
|
8
|
+
* Today the producers are row triggers that core generates for a registered
|
|
9
|
+
* table. They catch every writer: actions, sync jobs, raw SQL, and deletes.
|
|
10
|
+
* If core later gains a data layer that every write goes through, it calls the
|
|
11
|
+
* same function and the triggers retire; consumers don't change.
|
|
12
|
+
*
|
|
13
|
+
* Each (consumer, resource) pair is one row. Repeated changes update that row
|
|
14
|
+
* rather than adding rows, so autosave doesn't grow the feed. Every change
|
|
15
|
+
* takes a new value from one sequence, taken after the row lock, so for any
|
|
16
|
+
* one resource a larger `seq` always means a later committed change. Consumers
|
|
17
|
+
* use it to reject stale work and to delete only what they processed.
|
|
18
|
+
*
|
|
19
|
+
* A change that fails is retried with backoff, and after its last allowed
|
|
20
|
+
* attempt it is marked failed but still retried every few minutes. It stays
|
|
21
|
+
* pending the whole time, so a consumer that reports freshness never treats
|
|
22
|
+
* it as done.
|
|
23
|
+
*
|
|
24
|
+
* Consumers never poll. They process changes only where the database is
|
|
25
|
+
* already awake: before a read that needs fresh data, right after a write, or
|
|
26
|
+
* inside the framework's recurring sweep. See docs/search-architecture.md.
|
|
27
|
+
*/
|
|
28
|
+
import { type DbExec } from "../db/client.js";
|
|
29
|
+
export declare const RESOURCE_CHANGES_TABLE = "app_resource_changes";
|
|
30
|
+
export declare const RESOURCE_CHANGE_CONSUMERS_TABLE = "app_resource_change_consumers";
|
|
31
|
+
export declare const RESOURCE_CHANGE_SEQUENCE = "app_resource_change_seq";
|
|
32
|
+
export declare const RESOURCE_CHANGED_FUNCTION = "agent_native_app_resource_changed";
|
|
33
|
+
/** After this many attempts a change is marked failed. */
|
|
34
|
+
export declare const RESOURCE_CHANGE_MAX_ATTEMPTS = 5;
|
|
35
|
+
/**
|
|
36
|
+
* Creates the feed's tables, sequence, and producer function. Listed in
|
|
37
|
+
* `server/release-schema.ts`; trigger installation calls it too, so the
|
|
38
|
+
* function a trigger calls always exists before the trigger does.
|
|
39
|
+
*/
|
|
40
|
+
export declare function ensureResourceChangeTables(injectedClient?: DbExec): Promise<void>;
|
|
41
|
+
/** Where a registered resource lives, for generating its triggers. */
|
|
42
|
+
export interface ResourceChangeSource {
|
|
43
|
+
app: string;
|
|
44
|
+
resourceType: string;
|
|
45
|
+
table: string;
|
|
46
|
+
idColumn: string;
|
|
47
|
+
}
|
|
48
|
+
export declare function assertResourceKey(value: string, label: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* Names of the function and triggers generated for one source. Functions are
|
|
51
|
+
* schema-wide and the readable part is shortened to fit Postgres's 63
|
|
52
|
+
* characters, so a hash of the app, table, and type keeps every source's
|
|
53
|
+
* names distinct. Installing a source replaces whatever has its names, so
|
|
54
|
+
* the hash is wide enough that two sources never share them.
|
|
55
|
+
*/
|
|
56
|
+
export declare function resourceChangeTriggerNames(source: ResourceChangeSource): {
|
|
57
|
+
function: string;
|
|
58
|
+
insertDeleteTrigger: string;
|
|
59
|
+
updateTrigger: string;
|
|
60
|
+
truncateTrigger: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* SQL that makes every committed insert, update, delete, and truncate on the
|
|
64
|
+
* source table call the producer function. An update that changes nothing is
|
|
65
|
+
* skipped. An update that changes the id reports both ids. A truncate
|
|
66
|
+
* reports every row it removes as deleted.
|
|
67
|
+
*
|
|
68
|
+
* "Changes nothing" compares the rows' stored bytes (`*<>`), not their
|
|
69
|
+
* values: a `json` or `point` column has no equality operator, and comparing
|
|
70
|
+
* such rows by value would make every update on the table fail.
|
|
71
|
+
*
|
|
72
|
+
* Triggers are replaced in place, never dropped and recreated: a write
|
|
73
|
+
* committed between a drop and a create would never be recorded.
|
|
74
|
+
*/
|
|
75
|
+
export declare function resourceChangeTriggerSql(source: ResourceChangeSource): string[];
|
|
76
|
+
/**
|
|
77
|
+
* Installs change capture for a source and subscribes a consumer to it.
|
|
78
|
+
* Apps call this from a named migration, so the triggers ship with the app's
|
|
79
|
+
* schema rather than being created on a request path.
|
|
80
|
+
*
|
|
81
|
+
* Creating a trigger waits for every open transaction on the table, and
|
|
82
|
+
* queues the table's writes behind it while it waits, so each waits at most
|
|
83
|
+
* a few seconds. Returns false when one gave up; the caller retries later.
|
|
84
|
+
*/
|
|
85
|
+
export declare function installResourceChangeCapture(exec: DbExec, source: ResourceChangeSource, consumer: string): Promise<boolean>;
|
|
86
|
+
export declare function subscribeResourceChangeConsumer(exec: DbExec, source: Pick<ResourceChangeSource, "app" | "resourceType">, consumer: string): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* True when every generated trigger exists on the source table and fires.
|
|
89
|
+
* A disabled trigger (`ALTER TABLE ... DISABLE TRIGGER`) records nothing, so
|
|
90
|
+
* it counts as missing.
|
|
91
|
+
*/
|
|
92
|
+
export declare function resourceChangeCaptureInstalled(exec: DbExec, source: ResourceChangeSource): Promise<boolean>;
|
|
93
|
+
export interface ResourceChangeFeed {
|
|
94
|
+
consumer: string;
|
|
95
|
+
app: string;
|
|
96
|
+
resourceType: string;
|
|
97
|
+
}
|
|
98
|
+
export interface ClaimedResourceChange {
|
|
99
|
+
resourceId: string;
|
|
100
|
+
seq: string;
|
|
101
|
+
attempts: number;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* A condition a consumer attaches to its claim, complete, and fail
|
|
105
|
+
* statements. It is checked inside each statement, so once it stops holding,
|
|
106
|
+
* those statements change nothing. Search uses it so a process running an
|
|
107
|
+
* older index version can't consume changes queued for a newer one.
|
|
108
|
+
*/
|
|
109
|
+
export interface ResourceChangeFence {
|
|
110
|
+
sql: string;
|
|
111
|
+
args: unknown[];
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Leases up to `limit` ready changes, oldest first. Rows another claimant is
|
|
115
|
+
* locking right now are skipped, and the lease expires on its own if this
|
|
116
|
+
* process dies, so there is nothing to clean up.
|
|
117
|
+
*/
|
|
118
|
+
export declare function claimResourceChanges(exec: DbExec, feed: ResourceChangeFeed, limit: number, fence?: ResourceChangeFence): Promise<ClaimedResourceChange[]>;
|
|
119
|
+
export declare function compareSeq(a: string, b: string): number;
|
|
120
|
+
/**
|
|
121
|
+
* Deletes processed changes, but only rows whose seq is still the one that
|
|
122
|
+
* was claimed. A change recorded meanwhile stays for the next pass.
|
|
123
|
+
*/
|
|
124
|
+
export declare function completeResourceChanges(exec: DbExec, feed: ResourceChangeFeed, changes: readonly ClaimedResourceChange[], fence?: ResourceChangeFence): Promise<void>;
|
|
125
|
+
/**
|
|
126
|
+
* Backs off failed changes, doubling the wait up to five minutes. After the
|
|
127
|
+
* last allowed attempt a change is also marked failed, which a consumer can
|
|
128
|
+
* report, but it keeps being retried: a failure that was only temporary
|
|
129
|
+
* clears itself, and a later write to the resource starts it fresh.
|
|
130
|
+
*/
|
|
131
|
+
export declare function failResourceChanges(exec: DbExec, feed: ResourceChangeFeed, changes: readonly ClaimedResourceChange[], fence?: ResourceChangeFence): Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* Two boolean columns for a caller's own SELECT, so checking the feed costs
|
|
134
|
+
* no extra round trip: `pending` while any change is waiting, leased, or
|
|
135
|
+
* backing off, and `failing` while any has used up its attempts.
|
|
136
|
+
*/
|
|
137
|
+
export declare function resourceChangeBacklogColumns(feed: ResourceChangeFeed): {
|
|
138
|
+
sql: string;
|
|
139
|
+
args: unknown[];
|
|
140
|
+
};
|
|
141
|
+
/** True while any change at or below `seq` is still pending for this feed. */
|
|
142
|
+
export declare function hasPendingResourceChanges(exec: DbExec, feed: ResourceChangeFeed, options?: {
|
|
143
|
+
atOrBelowSeq?: string;
|
|
144
|
+
}): Promise<boolean>;
|
|
145
|
+
/**
|
|
146
|
+
* Records a change for every row of the source table, for this consumer
|
|
147
|
+
* only, and returns a seq at or above every one it assigned. Consumers use
|
|
148
|
+
* it to rebuild from scratch.
|
|
149
|
+
*
|
|
150
|
+
* A change already queued is replaced by a fresh one, even if another
|
|
151
|
+
* process has it leased or it has failed: that work was done for the old
|
|
152
|
+
* consumer state, so it must not complete what the rebuild queued.
|
|
153
|
+
*/
|
|
154
|
+
export declare function enqueueAllResourceChanges(exec: DbExec, source: ResourceChangeSource, consumer: string, reason: string): Promise<string>;
|
|
155
|
+
type DrainHook = () => Promise<unknown>;
|
|
156
|
+
/**
|
|
157
|
+
* Registers work to run right after a request that changed data, while the
|
|
158
|
+
* database is known to be awake. Returns an unregister function.
|
|
159
|
+
*/
|
|
160
|
+
export declare function registerAfterWriteDrain(id: string, drain: DrainHook): () => void;
|
|
161
|
+
/**
|
|
162
|
+
* Runs registered after-write drains without delaying the caller. On
|
|
163
|
+
* serverless the request's `waitUntil` keeps the function alive for them.
|
|
164
|
+
*/
|
|
165
|
+
export declare function runAfterWriteDrains(waitUntil?: (promise: Promise<unknown>) => void): void;
|
|
166
|
+
export {};
|