@query-farm/vgi 0.28.0 → 0.29.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/README.md +26 -7
- package/dist/arguments/argument-spec.d.ts +53 -0
- package/dist/arrow/impl-arrowjs/index.d.ts +1 -1
- package/dist/arrow/impl-arrowjs/iterate.d.ts +10 -1
- package/dist/arrow/impl-arrowjs/schema.d.ts +2 -0
- package/dist/arrow/impl-flechette/index.d.ts +1 -1
- package/dist/arrow/impl-flechette/iterate.d.ts +10 -1
- package/dist/arrow/impl-flechette/schema.d.ts +2 -0
- package/dist/arrow/index.d.ts +1 -1
- package/dist/arrow/schema-types.d.ts +3 -1
- package/dist/arrow/types.d.ts +39 -3
- package/dist/catalog/interface.d.ts +43 -0
- package/dist/client/client.d.ts +1 -1
- package/dist/client-entry.js +360 -178
- package/dist/client-entry.js.map +14 -14
- package/dist/functions/table.d.ts +63 -0
- package/dist/functions/types.d.ts +38 -0
- package/dist/generated/vgi-client.d.ts +59 -0
- package/dist/generated/vgi-protocol-schemas.d.ts +19 -0
- package/dist/http/fetch.d.ts +10 -0
- package/dist/index.core.d.ts +2 -2
- package/dist/index.js +1203 -834
- package/dist/index.js.map +40 -37
- package/dist/protocol/handlers/catalog/shared.d.ts +0 -8
- package/dist/protocol/handlers/function.d.ts +11 -0
- package/dist/protocol/serializers/bind.d.ts +1 -2
- package/dist/protocol/serializers/cardinality.d.ts +1 -2
- package/dist/protocol/serializers/splits.d.ts +126 -0
- package/dist/protocol/types.d.ts +27 -0
- package/dist/request-auth.d.ts +28 -0
- package/dist/serve-entry.d.ts +4 -0
- package/dist/serve-entry.js +745 -359
- package/dist/serve-entry.js.map +32 -29
- package/dist/split-token.d.ts +88 -0
- package/dist/worker-cf-entry.js +1029 -619
- package/dist/worker-cf-entry.js.map +37 -34
- package/package.json +3 -3
- package/src/arguments/argument-spec.ts +81 -0
- package/src/arguments/arguments.ts +0 -1
- package/src/arrow/impl-arrowjs/index.ts +1 -1
- package/src/arrow/impl-arrowjs/iterate.ts +21 -0
- package/src/arrow/impl-arrowjs/schema.ts +4 -0
- package/src/arrow/impl-flechette/index.ts +1 -1
- package/src/arrow/impl-flechette/iterate.ts +24 -1
- package/src/arrow/impl-flechette/schema.ts +4 -0
- package/src/arrow/index.ts +1 -1
- package/src/arrow/schema-types.ts +4 -0
- package/src/arrow/types.ts +39 -3
- package/src/catalog/interface.ts +124 -29
- package/src/catalog/read-only.ts +18 -18
- package/src/client/client.ts +6 -2
- package/src/functions/aggregate.ts +3 -0
- package/src/functions/copy-from.ts +2 -1
- package/src/functions/copy-to.ts +2 -1
- package/src/functions/scalar.ts +11 -0
- package/src/functions/table-buffering.ts +3 -0
- package/src/functions/table-in-out.ts +8 -4
- package/src/functions/table.ts +129 -2
- package/src/functions/types.ts +40 -0
- package/src/generated/vgi-client.ts +63 -1
- package/src/generated/vgi-protocol-schemas.ts +240 -14
- package/src/http/fetch.ts +117 -2
- package/src/index.core.ts +2 -2
- package/src/protocol/handlers/aggregate.ts +10 -5
- package/src/protocol/handlers/catalog/admin.ts +41 -48
- package/src/protocol/handlers/catalog/index_methods.ts +4 -11
- package/src/protocol/handlers/catalog/macro.ts +8 -16
- package/src/protocol/handlers/catalog/shared.ts +10 -57
- package/src/protocol/handlers/catalog/table.ts +33 -96
- package/src/protocol/handlers/catalog/view.ts +13 -17
- package/src/protocol/handlers/function.ts +311 -18
- package/src/protocol/handlers/table-buffering.ts +7 -4
- package/src/protocol/serializers/bind.ts +56 -63
- package/src/protocol/serializers/cardinality.ts +7 -7
- package/src/protocol/serializers/init.ts +52 -10
- package/src/protocol/serializers/splits.ts +219 -0
- package/src/protocol/state-serializer.ts +55 -1
- package/src/protocol/types.ts +27 -0
- package/src/request-auth.ts +73 -0
- package/src/serve-entry.ts +5 -0
- package/src/split-token.ts +297 -0
- package/src/worker-cf-entry.ts +2 -1
- package/src/worker.ts +30 -10
|
@@ -60,11 +60,3 @@ export declare function makeGetCatalog(catalog: CatalogInterface | undefined): G
|
|
|
60
60
|
*/
|
|
61
61
|
export declare function decodeOptionsBatch(bytes: any): Record<string, unknown>;
|
|
62
62
|
export declare const emptyResultSchema: VgiSchema;
|
|
63
|
-
export declare const attachOpaqueDataParam: VgiSchema;
|
|
64
|
-
export declare const attachOpaqueDataTxnParams: VgiSchema;
|
|
65
|
-
export declare const attachOpaqueDataNameTxnParams: VgiSchema;
|
|
66
|
-
export declare const attachOpaqueDataSchemaNameTxnParams: VgiSchema;
|
|
67
|
-
export declare const schemaNameIgnoreNotFoundTxnParams: VgiSchema;
|
|
68
|
-
export declare const schemaNameCommentParams: VgiSchema;
|
|
69
|
-
export declare const schemaNameRenameParams: VgiSchema;
|
|
70
|
-
export declare const columnOpParams: VgiSchema;
|
|
@@ -11,6 +11,17 @@ export interface FunctionHandlerConfig {
|
|
|
11
11
|
*/
|
|
12
12
|
catalogInterface?: {
|
|
13
13
|
catalogNameForAttach(a: Uint8Array): string | null;
|
|
14
|
+
/**
|
|
15
|
+
* The catalog's current version, which is the consistency anchor every split
|
|
16
|
+
* token is stamped with AND checked against.
|
|
17
|
+
*
|
|
18
|
+
* Both sides read it from here on purpose. Minting from a different value
|
|
19
|
+
* than redemption compares against is not a subtle bug: it refuses every
|
|
20
|
+
* token, and the documented response to SPLIT_SNAPSHOT_EXPIRED is "re-run
|
|
21
|
+
* the query", which re-plans, mints the same mismatch and fails again — a
|
|
22
|
+
* livelock returning no rows, blaming the data for moving when it has not.
|
|
23
|
+
*/
|
|
24
|
+
version?(attach: Uint8Array, txn?: Uint8Array): number | Promise<number>;
|
|
14
25
|
};
|
|
15
26
|
}
|
|
16
27
|
export declare function registerFunctionMethods(protocol: Protocol, config: FunctionHandlerConfig): void;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { type VgiSchema, type VgiBatch } from "../../arrow/index.js";
|
|
2
2
|
import type { BindRequest, BindResponse } from "../types.js";
|
|
3
3
|
declare const BIND_REQUEST_SCHEMA: VgiSchema;
|
|
4
|
-
declare const BIND_RESPONSE_SCHEMA: VgiSchema;
|
|
5
4
|
export declare function serializeBindRequest(req: BindRequest): VgiBatch;
|
|
6
5
|
export declare function deserializeBindRequest(params: Record<string, any>): BindRequest;
|
|
7
6
|
export declare function serializeBindResponse(resp: BindResponse): Record<string, any>;
|
|
8
7
|
export declare function deserializeBindResponse(params: Record<string, any>): BindResponse;
|
|
9
|
-
export { BIND_REQUEST_SCHEMA
|
|
8
|
+
export { BIND_REQUEST_SCHEMA };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type VgiSchema } from "../../arrow/index.js";
|
|
2
2
|
import type { TableFunctionCardinalityRequest, TableCardinality } from "../types.js";
|
|
3
|
-
declare const TABLE_CARDINALITY_SCHEMA: VgiSchema;
|
|
4
3
|
declare const TABLE_FUNCTION_CARDINALITY_REQUEST_SCHEMA: VgiSchema;
|
|
5
4
|
export declare function deserializeCardinalityRequest(params: Record<string, any>): TableFunctionCardinalityRequest;
|
|
6
5
|
export declare function serializeTableCardinality(card: TableCardinality): Record<string, any>;
|
|
7
|
-
export {
|
|
6
|
+
export { TABLE_FUNCTION_CARDINALITY_REQUEST_SCHEMA };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { type VgiSchema } from "../../arrow/index.js";
|
|
2
|
+
import type { BindRequest } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* One named, independently redeemable unit of scan work.
|
|
5
|
+
*
|
|
6
|
+
* A worker sets `payload` and nothing else; the framework stamps `token` from
|
|
7
|
+
* it, so an author cannot forget the consistency anchor or mis-bind the
|
|
8
|
+
* fingerprint. The client sends the TOKEN back, never the raw payload.
|
|
9
|
+
*/
|
|
10
|
+
export interface ScanSplit {
|
|
11
|
+
/** The worker's own bytes NAMING this unit of work. */
|
|
12
|
+
payload: Uint8Array;
|
|
13
|
+
/** Framework-stamped envelope. Set by the framework, never by a worker. */
|
|
14
|
+
token?: Uint8Array;
|
|
15
|
+
estimatedRows?: number | bigint | null;
|
|
16
|
+
/** True if `estimatedRows` is exact — unlocks COUNT(*) from statistics. */
|
|
17
|
+
rowsExact?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Load-bearing for engines that bin-pack (DataFusion weight, Trino
|
|
20
|
+
* SplitWeight). Omitting it degrades them to round-robin by count; a greedily
|
|
21
|
+
* claiming client needs no cost model at all.
|
|
22
|
+
*/
|
|
23
|
+
estimatedBytes?: number | bigint | null;
|
|
24
|
+
/** 2-row (min, max) batch in the existing `vgi_partition_values` encoding. */
|
|
25
|
+
partitionBounds?: Uint8Array | null;
|
|
26
|
+
columnStatistics?: Uint8Array | null;
|
|
27
|
+
locationIds?: number[] | null;
|
|
28
|
+
startPosition?: Uint8Array | null;
|
|
29
|
+
/** Absent means UNBOUNDED — a shard read forever. */
|
|
30
|
+
endPosition?: Uint8Array | null;
|
|
31
|
+
}
|
|
32
|
+
/** What a table function's `plan` hook returns. */
|
|
33
|
+
export interface PlanResult {
|
|
34
|
+
/**
|
|
35
|
+
* One entry per unit of work. EMPTY is legal and means "no work": a
|
|
36
|
+
* fully-pruned scan reaches it, and the client must produce an empty result
|
|
37
|
+
* rather than an error.
|
|
38
|
+
*/
|
|
39
|
+
splits: ScanSplit[];
|
|
40
|
+
/**
|
|
41
|
+
* Continued enumeration. More than one MUST partition the remaining
|
|
42
|
+
* enumeration disjointly and exhaustively. No client checks this: a dedup was
|
|
43
|
+
* tried and removed (it needed a set holding a copy of every token, it compared
|
|
44
|
+
* token bytes so it could never work on a keyed worker where each mint uses a
|
|
45
|
+
* fresh nonce, and the most a client can do with a duplicate is refuse anyway).
|
|
46
|
+
* Violating it returns DUPLICATE ROWS, silently.
|
|
47
|
+
*/
|
|
48
|
+
nextCursors?: Uint8Array[];
|
|
49
|
+
/** NORMATIVE cap on redemption concurrency, not advisory. */
|
|
50
|
+
maxWorkers?: number | null;
|
|
51
|
+
estimatedTotalSplits?: number | bigint | null;
|
|
52
|
+
estimatedTotalRows?: number | bigint | null;
|
|
53
|
+
estimatedTotalBytes?: number | bigint | null;
|
|
54
|
+
/**
|
|
55
|
+
* The counter a stale token is detected against, and the anchor the framework
|
|
56
|
+
* stamps into every token in this plan.
|
|
57
|
+
*/
|
|
58
|
+
catalogVersion?: number | bigint | null;
|
|
59
|
+
/**
|
|
60
|
+
* `catalog` (the default) or `transaction`. A transaction-scoped plan is not
|
|
61
|
+
* cacheable and is not redeemable after commit or rollback.
|
|
62
|
+
*/
|
|
63
|
+
scope?: "catalog" | "transaction";
|
|
64
|
+
cacheMaxAgeSeconds?: number | null;
|
|
65
|
+
}
|
|
66
|
+
/** The request a client sends to divide a scan. */
|
|
67
|
+
export interface TableFunctionPlanRequest {
|
|
68
|
+
bind_call: BindRequest;
|
|
69
|
+
bind_opaque_data: Uint8Array | null;
|
|
70
|
+
projection_ids: number[] | null;
|
|
71
|
+
pushdown_filters: Uint8Array | null;
|
|
72
|
+
/** The primary sizing lever: every engine is byte-driven. */
|
|
73
|
+
target_split_bytes: bigint | null;
|
|
74
|
+
/**
|
|
75
|
+
* The parallelism FLOOR — a small but expensive table still needs one reader
|
|
76
|
+
* per thread, which a byte target alone would not give it.
|
|
77
|
+
*/
|
|
78
|
+
min_splits: bigint | null;
|
|
79
|
+
/**
|
|
80
|
+
* A place in the ENUMERATION of splits — NOT a place in the data. A cursor
|
|
81
|
+
* lives for one plan call; a position is checkpointed and must survive
|
|
82
|
+
* restarts, upgrades and key rotation.
|
|
83
|
+
*/
|
|
84
|
+
cursor: Uint8Array | null;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The ScanSplit shape, from codegen rather than hand-written.
|
|
88
|
+
*
|
|
89
|
+
* It used to be spelled out here, and hand-written is how four SDKs ended up
|
|
90
|
+
* disagreeing about which of these columns were binary and which were
|
|
91
|
+
* large_binary — a disagreement the client surfaced as "the worker bypassed the
|
|
92
|
+
* framework", on every scan.
|
|
93
|
+
*/
|
|
94
|
+
export declare const SCAN_SPLIT_SCHEMA: VgiSchema;
|
|
95
|
+
/**
|
|
96
|
+
* The PlanResponse shape, taken from codegen rather than hand-written: it is
|
|
97
|
+
* regenerated from the canonical protocol, so it cannot drift.
|
|
98
|
+
*/
|
|
99
|
+
export declare const PLAN_RESPONSE_SCHEMA: VgiSchema;
|
|
100
|
+
/**
|
|
101
|
+
* Decode a plan request.
|
|
102
|
+
*
|
|
103
|
+
* A client sends only the sizing inputs it actually has — the DuckDB extension
|
|
104
|
+
* sends seven columns, because it has no basis to invent a byte target it did
|
|
105
|
+
* not set — so every field but `bind_call` is read defensively.
|
|
106
|
+
*/
|
|
107
|
+
export declare function deserializePlanRequest(params: Record<string, any>): TableFunctionPlanRequest;
|
|
108
|
+
/**
|
|
109
|
+
* The raw bind fields the split-token fingerprint is derived from.
|
|
110
|
+
*
|
|
111
|
+
* Read from the UNDECODED bind_call dict rather than the parsed BindRequest,
|
|
112
|
+
* because the fingerprint must be reproducible byte-for-byte between the plan
|
|
113
|
+
* that minted a token and the init that redeems it. A decoded-then-re-encoded
|
|
114
|
+
* argument list is not guaranteed to round-trip to identical bytes; the wire
|
|
115
|
+
* bytes are.
|
|
116
|
+
*/
|
|
117
|
+
export declare function fingerprintInputs(bindCallBytes: Uint8Array): {
|
|
118
|
+
schemaName: string;
|
|
119
|
+
functionName: string;
|
|
120
|
+
args: Uint8Array;
|
|
121
|
+
settings: Uint8Array;
|
|
122
|
+
};
|
|
123
|
+
/** Build the row dict for one `ScanSplit`. */
|
|
124
|
+
export declare function scanSplitRow(split: ScanSplit, token: Uint8Array): Record<string, any>;
|
|
125
|
+
/** Build the row dict for a `PlanResponse`, given already-serialized splits. */
|
|
126
|
+
export declare function planResponseRow(result: PlanResult, splitBlobs: Uint8Array[]): Record<string, any>;
|
package/dist/protocol/types.d.ts
CHANGED
|
@@ -108,6 +108,33 @@ export interface InitRequest {
|
|
|
108
108
|
* bytes the worker's combine() chose.
|
|
109
109
|
*/
|
|
110
110
|
finalize_state_id: Uint8Array | null;
|
|
111
|
+
/**
|
|
112
|
+
* The framework-stamped split envelopes this init redeems, or null when this
|
|
113
|
+
* is not a split init.
|
|
114
|
+
*
|
|
115
|
+
* A LIST because an engine whose partition count IS its concurrency bin-packs
|
|
116
|
+
* at planning time and reads a whole group per partition; DuckDB always sends
|
|
117
|
+
* exactly one. A worker handed several concatenates them in the order given.
|
|
118
|
+
*/
|
|
119
|
+
split_tokens: Uint8Array[] | null;
|
|
120
|
+
/**
|
|
121
|
+
* The VERIFIED payloads behind `split_tokens` — the worker's own bytes, with
|
|
122
|
+
* the envelope already opened and stripped.
|
|
123
|
+
*
|
|
124
|
+
* Populated by the protocol handler, which is what holds the signing key, so
|
|
125
|
+
* an unverified token never reaches a function. Null when this is not a split
|
|
126
|
+
* init.
|
|
127
|
+
*/
|
|
128
|
+
split_payloads?: Uint8Array[] | null;
|
|
129
|
+
/**
|
|
130
|
+
* A plain fetch limit, when the engine can supply one.
|
|
131
|
+
*
|
|
132
|
+
* Always null from DuckDB — `TableFunctionInitInput` carries no limit, and
|
|
133
|
+
* the Top-N `order_by_limit` means something different ("top K by that
|
|
134
|
+
* column"). DataFusion supplies a real value via `TableProvider::scan(limit)`.
|
|
135
|
+
* Over-producing is legal; the engine re-applies the limit above the scan.
|
|
136
|
+
*/
|
|
137
|
+
row_limit: number | null;
|
|
111
138
|
execution_id: Uint8Array | null;
|
|
112
139
|
init_opaque_data: Uint8Array | null;
|
|
113
140
|
/**
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AuthContext } from "@query-farm/vgi-rpc";
|
|
2
|
+
/** Mutable per-request cell. The scope is opened before the identity is known
|
|
3
|
+
* (authentication happens inside the handler), so the holder is published
|
|
4
|
+
* first and filled in when the framework reports the principal. */
|
|
5
|
+
export interface RequestAuthHolder {
|
|
6
|
+
auth?: AuthContext;
|
|
7
|
+
}
|
|
8
|
+
/** The slice of `AsyncLocalStorage` this module needs. Structural on purpose:
|
|
9
|
+
* it keeps `node:async_hooks` out of every non-HTTP build, and lets a test
|
|
10
|
+
* install a trivial stub. */
|
|
11
|
+
export interface RequestAuthScope {
|
|
12
|
+
getStore(): RequestAuthHolder | undefined;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Publish the scope `currentRequestAuth()` reads. Called once per process by
|
|
16
|
+
* the HTTP entry point; idempotent for the same scope.
|
|
17
|
+
*/
|
|
18
|
+
export declare function setRequestAuthScope(next: RequestAuthScope | undefined): void;
|
|
19
|
+
/**
|
|
20
|
+
* The authenticated principal of the request being served on this async
|
|
21
|
+
* context, or `undefined` when there is none — no scope installed (stdio /
|
|
22
|
+
* AF_UNIX / a host that has no `AsyncLocalStorage`), or an unauthenticated
|
|
23
|
+
* request.
|
|
24
|
+
*
|
|
25
|
+
* `undefined` and an anonymous `AuthContext` produce the same identity tail, so
|
|
26
|
+
* the two cases need not be distinguished by callers.
|
|
27
|
+
*/
|
|
28
|
+
export declare function currentRequestAuth(): AuthContext | undefined;
|
package/dist/serve-entry.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CatalogInterface } from "./catalog/interface.js";
|
|
2
2
|
import type { FunctionRegistry } from "./functions/registry.js";
|
|
3
|
+
import type { AuthenticateFn } from "@query-farm/vgi-rpc";
|
|
3
4
|
/** Environment variables `serveVgiWorker` reads. Injectable for testing. */
|
|
4
5
|
export interface ServeEnv {
|
|
5
6
|
PORT?: string;
|
|
@@ -40,6 +41,9 @@ export interface ServeVgiWorkerOptions {
|
|
|
40
41
|
quiet?: boolean;
|
|
41
42
|
/** Environment source (default `process.env`). */
|
|
42
43
|
env?: ServeEnv;
|
|
44
|
+
/** Authenticates each request, returning the caller's `AuthContext`.
|
|
45
|
+
* Omit for an anonymous worker (the default). */
|
|
46
|
+
authenticate?: AuthenticateFn;
|
|
43
47
|
}
|
|
44
48
|
/** The slice of `Bun.serve`'s return value this module uses. */
|
|
45
49
|
export interface VgiHttpServer {
|