@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.
Files changed (83) hide show
  1. package/README.md +26 -7
  2. package/dist/arguments/argument-spec.d.ts +53 -0
  3. package/dist/arrow/impl-arrowjs/index.d.ts +1 -1
  4. package/dist/arrow/impl-arrowjs/iterate.d.ts +10 -1
  5. package/dist/arrow/impl-arrowjs/schema.d.ts +2 -0
  6. package/dist/arrow/impl-flechette/index.d.ts +1 -1
  7. package/dist/arrow/impl-flechette/iterate.d.ts +10 -1
  8. package/dist/arrow/impl-flechette/schema.d.ts +2 -0
  9. package/dist/arrow/index.d.ts +1 -1
  10. package/dist/arrow/schema-types.d.ts +3 -1
  11. package/dist/arrow/types.d.ts +39 -3
  12. package/dist/catalog/interface.d.ts +43 -0
  13. package/dist/client/client.d.ts +1 -1
  14. package/dist/client-entry.js +360 -178
  15. package/dist/client-entry.js.map +14 -14
  16. package/dist/functions/table.d.ts +63 -0
  17. package/dist/functions/types.d.ts +38 -0
  18. package/dist/generated/vgi-client.d.ts +59 -0
  19. package/dist/generated/vgi-protocol-schemas.d.ts +19 -0
  20. package/dist/http/fetch.d.ts +10 -0
  21. package/dist/index.core.d.ts +2 -2
  22. package/dist/index.js +1203 -834
  23. package/dist/index.js.map +40 -37
  24. package/dist/protocol/handlers/catalog/shared.d.ts +0 -8
  25. package/dist/protocol/handlers/function.d.ts +11 -0
  26. package/dist/protocol/serializers/bind.d.ts +1 -2
  27. package/dist/protocol/serializers/cardinality.d.ts +1 -2
  28. package/dist/protocol/serializers/splits.d.ts +126 -0
  29. package/dist/protocol/types.d.ts +27 -0
  30. package/dist/request-auth.d.ts +28 -0
  31. package/dist/serve-entry.d.ts +4 -0
  32. package/dist/serve-entry.js +745 -359
  33. package/dist/serve-entry.js.map +32 -29
  34. package/dist/split-token.d.ts +88 -0
  35. package/dist/worker-cf-entry.js +1029 -619
  36. package/dist/worker-cf-entry.js.map +37 -34
  37. package/package.json +3 -3
  38. package/src/arguments/argument-spec.ts +81 -0
  39. package/src/arguments/arguments.ts +0 -1
  40. package/src/arrow/impl-arrowjs/index.ts +1 -1
  41. package/src/arrow/impl-arrowjs/iterate.ts +21 -0
  42. package/src/arrow/impl-arrowjs/schema.ts +4 -0
  43. package/src/arrow/impl-flechette/index.ts +1 -1
  44. package/src/arrow/impl-flechette/iterate.ts +24 -1
  45. package/src/arrow/impl-flechette/schema.ts +4 -0
  46. package/src/arrow/index.ts +1 -1
  47. package/src/arrow/schema-types.ts +4 -0
  48. package/src/arrow/types.ts +39 -3
  49. package/src/catalog/interface.ts +124 -29
  50. package/src/catalog/read-only.ts +18 -18
  51. package/src/client/client.ts +6 -2
  52. package/src/functions/aggregate.ts +3 -0
  53. package/src/functions/copy-from.ts +2 -1
  54. package/src/functions/copy-to.ts +2 -1
  55. package/src/functions/scalar.ts +11 -0
  56. package/src/functions/table-buffering.ts +3 -0
  57. package/src/functions/table-in-out.ts +8 -4
  58. package/src/functions/table.ts +129 -2
  59. package/src/functions/types.ts +40 -0
  60. package/src/generated/vgi-client.ts +63 -1
  61. package/src/generated/vgi-protocol-schemas.ts +240 -14
  62. package/src/http/fetch.ts +117 -2
  63. package/src/index.core.ts +2 -2
  64. package/src/protocol/handlers/aggregate.ts +10 -5
  65. package/src/protocol/handlers/catalog/admin.ts +41 -48
  66. package/src/protocol/handlers/catalog/index_methods.ts +4 -11
  67. package/src/protocol/handlers/catalog/macro.ts +8 -16
  68. package/src/protocol/handlers/catalog/shared.ts +10 -57
  69. package/src/protocol/handlers/catalog/table.ts +33 -96
  70. package/src/protocol/handlers/catalog/view.ts +13 -17
  71. package/src/protocol/handlers/function.ts +311 -18
  72. package/src/protocol/handlers/table-buffering.ts +7 -4
  73. package/src/protocol/serializers/bind.ts +56 -63
  74. package/src/protocol/serializers/cardinality.ts +7 -7
  75. package/src/protocol/serializers/init.ts +52 -10
  76. package/src/protocol/serializers/splits.ts +219 -0
  77. package/src/protocol/state-serializer.ts +55 -1
  78. package/src/protocol/types.ts +27 -0
  79. package/src/request-auth.ts +73 -0
  80. package/src/serve-entry.ts +5 -0
  81. package/src/split-token.ts +297 -0
  82. package/src/worker-cf-entry.ts +2 -1
  83. package/src/worker.ts +30 -10
@@ -2,7 +2,7 @@
2
2
  // GENERATED by vgi.codegen.ts_schemas. DO NOT EDIT BY HAND.
3
3
  //
4
4
  // Generator: vgi-gen-ts-schemas v2
5
- // Content hash: ae533ce2196c
5
+ // Content hash: 0da278e5271a
6
6
  //
7
7
  // To regenerate:
8
8
  // uv run --project ~/Development/vgi-python vgi-gen-ts-schemas \
@@ -15,9 +15,11 @@ import {
15
15
  bool,
16
16
  dictionary,
17
17
  field,
18
+ float64,
18
19
  int16,
19
20
  int32,
20
21
  int64,
22
+ largeBinary,
21
23
  list,
22
24
  map,
23
25
  schema,
@@ -32,7 +34,7 @@ export const CatalogInfoSchema = schema([
32
34
  field("implementation_version", utf8(), true),
33
35
  field("data_version_spec", utf8(), true),
34
36
  field("attach_option_specs", list(field("item", binary(), true)), false),
35
- field("releases", list(field("item", struct([field("version", utf8(), false), field("released_at", timestamp(TimeUnit.MICROSECOND, "UTC"), false), field("summary", utf8(), false), field("notes_url", utf8(), true)]), true)), false),
37
+ field("releases", list(field("item", struct([field("version", utf8(), false), field("released_at", timestamp(TimeUnit.MICROSECOND, "UTC"), true), field("summary", utf8(), false), field("notes_url", utf8(), true)]), true)), false),
36
38
  field("source_url", utf8(), true),
37
39
  ]);
38
40
 
@@ -62,14 +64,14 @@ export const TableInfoSchema = schema([
62
64
  field("supports_delete", bool(), false),
63
65
  field("supports_returning", bool(), false),
64
66
  field("supports_column_statistics", bool(), false),
65
- field("scan_function", binary(), false),
66
- field("insert_function", binary(), false),
67
- field("update_function", binary(), false),
68
- field("delete_function", binary(), false),
69
- field("cardinality_estimate", int64(), false),
70
- field("cardinality_max", int64(), false),
71
- field("column_statistics", binary(), false),
72
- field("bind_result", binary(), false),
67
+ field("scan_function", binary(), true),
68
+ field("insert_function", binary(), true),
69
+ field("update_function", binary(), true),
70
+ field("delete_function", binary(), true),
71
+ field("cardinality_estimate", int64(), true),
72
+ field("cardinality_max", int64(), true),
73
+ field("column_statistics", binary(), true),
74
+ field("bind_result", binary(), true),
73
75
  field("required_filters", list(field("item", list(field("item", utf8(), true)), true)), false),
74
76
  ]);
75
77
 
@@ -103,8 +105,12 @@ export const FunctionInfoSchema = schema([
103
105
  field("late_materialization", bool(), true),
104
106
  field("supported_expression_filters", list(field("item", utf8(), true)), false),
105
107
  field("order_preservation", dictionary(utf8(), int16()), true),
106
- field("max_workers", int32(), false),
108
+ field("max_workers", int32(), true),
107
109
  field("supports_batch_index", bool(), false),
110
+ field("supports_splits", bool(), false),
111
+ field("filters_exactly_applied", bool(), false),
112
+ field("supports_positions", bool(), false),
113
+ field("split_token_ttl_seconds", int64(), true),
108
114
  field("partition_kind", dictionary(utf8(), int16()), false),
109
115
  field("order_dependent", dictionary(utf8(), int16()), false),
110
116
  field("distinct_dependent", dictionary(utf8(), int16()), false),
@@ -127,9 +133,9 @@ export const MacroInfoSchema = schema([
127
133
  field("schema_name", utf8(), false),
128
134
  field("macro_type", dictionary(utf8(), int16()), false),
129
135
  field("parameters", list(field("item", utf8(), true)), false),
130
- field("parameter_default_values", binary(), false),
136
+ field("parameter_default_values", binary(), true),
131
137
  field("definition", utf8(), false),
132
- field("arguments_schema", binary(), false),
138
+ field("arguments_schema", binary(), true),
133
139
  ]);
134
140
 
135
141
  // Origin: IndexInfo
@@ -179,6 +185,23 @@ export const ScanBranchSchema = schema([
179
185
  field("source_catalog", utf8(), true),
180
186
  field("source_schema", utf8(), true),
181
187
  field("source_table", utf8(), true),
188
+ field("format_name", utf8(), true),
189
+ field("format_locations", list(field("item", utf8(), true)), true),
190
+ field("format_options", binary(), true),
191
+ ]);
192
+
193
+ // Origin: ScanSplit
194
+ export const ScanSplitSchema = schema([
195
+ field("payload", binary(), false),
196
+ field("token", binary(), false),
197
+ field("estimated_rows", int64(), true),
198
+ field("rows_exact", bool(), false),
199
+ field("estimated_bytes", int64(), true),
200
+ field("partition_bounds", binary(), true),
201
+ field("column_statistics", binary(), true),
202
+ field("location_ids", list(field("item", int64(), true)), true),
203
+ field("start_position", binary(), true),
204
+ field("end_position", binary(), true),
182
205
  ]);
183
206
 
184
207
  // Origin: AttachCatalogInfo
@@ -192,6 +215,184 @@ export const AttachCatalogInfoSchema = schema([
192
215
  field("secret_ref", utf8(), false),
193
216
  ]);
194
217
 
218
+ // Origin: AggregateBindRequest
219
+ export const AggregateBindRequestSchema = schema([
220
+ field("function_name", utf8(), false),
221
+ field("arguments", binary(), false),
222
+ field("input_schema", binary(), true),
223
+ field("settings", binary(), true),
224
+ field("secrets", binary(), true),
225
+ field("attach_opaque_data", binary(), true),
226
+ field("schema_name", utf8(), true),
227
+ ]);
228
+
229
+ // Origin: AggregateCombineRequest
230
+ export const AggregateCombineRequestSchema = schema([
231
+ field("function_name", utf8(), false),
232
+ field("execution_id", binary(), false),
233
+ field("merge_batch", binary(), false),
234
+ field("attach_opaque_data", binary(), true),
235
+ field("schema_name", utf8(), true),
236
+ ]);
237
+
238
+ // Origin: AggregateDestructorRequest
239
+ export const AggregateDestructorRequestSchema = schema([
240
+ field("function_name", utf8(), false),
241
+ field("execution_id", binary(), false),
242
+ field("group_ids_batch", binary(), false),
243
+ field("attach_opaque_data", binary(), true),
244
+ field("schema_name", utf8(), true),
245
+ ]);
246
+
247
+ // Origin: AggregateFinalizeRequest
248
+ export const AggregateFinalizeRequestSchema = schema([
249
+ field("function_name", utf8(), false),
250
+ field("execution_id", binary(), false),
251
+ field("group_ids_batch", binary(), false),
252
+ field("output_schema", binary(), false),
253
+ field("attach_opaque_data", binary(), true),
254
+ field("schema_name", utf8(), true),
255
+ ]);
256
+
257
+ // Origin: AggregateUpdateRequest
258
+ export const AggregateUpdateRequestSchema = schema([
259
+ field("function_name", utf8(), false),
260
+ field("execution_id", binary(), false),
261
+ field("input_batch", binary(), false),
262
+ field("attach_opaque_data", binary(), true),
263
+ field("schema_name", utf8(), true),
264
+ ]);
265
+
266
+ // Origin: BindRequest
267
+ export const BindRequestSchema = schema([
268
+ field("function_name", utf8(), false),
269
+ field("arguments", binary(), false),
270
+ field("function_type", dictionary(utf8(), int16()), false),
271
+ field("input_schema", binary(), true),
272
+ field("settings", binary(), true),
273
+ field("secrets", binary(), true),
274
+ field("attach_opaque_data", binary(), true),
275
+ field("transaction_opaque_data", binary(), true),
276
+ field("resolved_secrets_provided", bool(), false),
277
+ field("at_unit", utf8(), true),
278
+ field("at_value", utf8(), true),
279
+ field("copy_from", struct([field("format", utf8(), false), field("file_path", utf8(), false), field("expected_schema", binary(), false)]), true),
280
+ field("copy_to", struct([field("format", utf8(), false), field("file_path", utf8(), false)]), true),
281
+ field("schema_name", utf8(), true),
282
+ ]);
283
+
284
+ // Origin: CatalogAttachRequest
285
+ export const CatalogAttachRequestSchema = schema([
286
+ field("name", utf8(), false),
287
+ field("options", binary(), true),
288
+ field("data_version_spec", utf8(), true),
289
+ field("implementation_version", utf8(), true),
290
+ field("client_capabilities", binary(), true),
291
+ ]);
292
+
293
+ // Origin: CopyFromContext
294
+ export const CopyFromContextSchema = schema([
295
+ field("format", utf8(), false),
296
+ field("file_path", utf8(), false),
297
+ field("expected_schema", binary(), false),
298
+ ]);
299
+
300
+ // Origin: CopyToContext
301
+ export const CopyToContextSchema = schema([
302
+ field("format", utf8(), false),
303
+ field("file_path", utf8(), false),
304
+ ]);
305
+
306
+ // Origin: GlobalInitResponse
307
+ export const GlobalInitResponseSchema = schema([
308
+ field("execution_id", binary(), false),
309
+ field("opaque_data", binary(), true),
310
+ field("max_workers", int64(), false),
311
+ ]);
312
+
313
+ // Origin: InitRequest
314
+ export const InitRequestSchema = schema([
315
+ field("bind_call", binary(), false),
316
+ field("output_schema", binary(), false),
317
+ field("bind_opaque_data", binary(), true),
318
+ field("projection_ids", list(field("item", int64(), true)), true),
319
+ field("pushdown_filters", largeBinary(), true),
320
+ field("join_keys", list(field("item", largeBinary(), true)), true),
321
+ field("split_tokens", list(field("item", largeBinary(), true)), true),
322
+ field("row_limit", int64(), true),
323
+ field("phase", dictionary(utf8(), int16()), true),
324
+ field("finalize_state_id", binary(), true),
325
+ field("execution_id", binary(), true),
326
+ field("init_opaque_data", binary(), true),
327
+ field("substream_id", binary(), true),
328
+ field("order_by_column_name", utf8(), true),
329
+ field("order_by_direction", dictionary(utf8(), int16()), true),
330
+ field("order_by_null_order", dictionary(utf8(), int16()), true),
331
+ field("order_by_limit", int64(), true),
332
+ field("tablesample_percentage", float64(), true),
333
+ field("tablesample_seed", int64(), true),
334
+ ]);
335
+
336
+ // Origin: TableBufferingCombineRequest
337
+ export const TableBufferingCombineRequestSchema = schema([
338
+ field("function_name", utf8(), false),
339
+ field("execution_id", binary(), false),
340
+ field("state_ids", list(field("item", binary(), true)), false),
341
+ field("attach_opaque_data", binary(), true),
342
+ field("transaction_id", binary(), true),
343
+ field("schema_name", utf8(), true),
344
+ ]);
345
+
346
+ // Origin: TableBufferingDestructorRequest
347
+ export const TableBufferingDestructorRequestSchema = schema([
348
+ field("function_name", utf8(), false),
349
+ field("execution_id", binary(), false),
350
+ field("attach_opaque_data", binary(), true),
351
+ field("transaction_id", binary(), true),
352
+ field("schema_name", utf8(), true),
353
+ ]);
354
+
355
+ // Origin: TableBufferingProcessRequest
356
+ export const TableBufferingProcessRequestSchema = schema([
357
+ field("function_name", utf8(), false),
358
+ field("execution_id", binary(), false),
359
+ field("input_batch", binary(), false),
360
+ field("attach_opaque_data", binary(), true),
361
+ field("transaction_id", binary(), true),
362
+ field("batch_index", int64(), true),
363
+ field("schema_name", utf8(), true),
364
+ ]);
365
+
366
+ // Origin: TableFunctionCardinalityRequest
367
+ export const TableFunctionCardinalityRequestSchema = schema([
368
+ field("bind_call", binary(), false),
369
+ field("bind_opaque_data", binary(), true),
370
+ ]);
371
+
372
+ // Origin: TableFunctionPlanRequest
373
+ export const TableFunctionPlanRequestSchema = schema([
374
+ field("bind_call", binary(), false),
375
+ field("bind_opaque_data", binary(), true),
376
+ field("projection_ids", list(field("item", int64(), true)), true),
377
+ field("pushdown_filters", largeBinary(), true),
378
+ field("join_keys", list(field("item", largeBinary(), true)), true),
379
+ field("row_limit", int64(), true),
380
+ field("target_split_bytes", int64(), true),
381
+ field("min_splits", int64(), true),
382
+ field("max_splits_per_response", int64(), true),
383
+ field("cursor", binary(), true),
384
+ field("refined_filters", largeBinary(), true),
385
+ field("filters_complete", bool(), false),
386
+ field("start_position", binary(), true),
387
+ field("end_position", binary(), true),
388
+ field("order_by_column_name", utf8(), true),
389
+ field("order_by_direction", dictionary(utf8(), int16()), true),
390
+ field("order_by_null_order", dictionary(utf8(), int16()), true),
391
+ field("order_by_limit", int64(), true),
392
+ field("tablesample_percentage", float64(), true),
393
+ field("tablesample_seed", int64(), true),
394
+ ]);
395
+
195
396
  // Origin: method 'aggregate_bind' result
196
397
  export const AggregateBindResultSchema = schema([
197
398
  field("output_schema", binary(), false),
@@ -244,7 +445,7 @@ export const AggregateWindowInitResultSchema = schema([]);
244
445
  // Origin: method 'bind' result
245
446
  export const BindResultSchema = schema([
246
447
  field("output_schema", binary(), false),
247
- field("opaque_data", binary(), false),
448
+ field("opaque_data", binary(), true),
248
449
  field("lookup_secret_types", list(field("item", utf8(), true)), false),
249
450
  field("lookup_scopes", list(field("item", utf8(), true)), false),
250
451
  field("lookup_names", list(field("item", utf8(), true)), false),
@@ -371,6 +572,26 @@ export const TableFunctionDynamicToStringResultSchema = schema([
371
572
  field("values", list(field("item", utf8(), true)), false),
372
573
  ]);
373
574
 
575
+ // Origin: method 'table_function_plan' result
576
+ export const TableFunctionPlanResultSchema = schema([
577
+ field("splits", list(field("item", binary(), true)), false),
578
+ field("next_cursors", list(field("item", binary(), true)), true),
579
+ field("execution_id", binary(), true),
580
+ field("init_opaque_data", binary(), true),
581
+ field("max_workers", int64(), true),
582
+ field("estimated_total_splits", int64(), true),
583
+ field("estimated_total_rows", int64(), true),
584
+ field("estimated_total_bytes", int64(), true),
585
+ field("catalog_version", int64(), true),
586
+ field("scope", utf8(), false),
587
+ field("locations", list(field("item", utf8(), true)), true),
588
+ field("partitioning", list(field("item", binary(), true)), true),
589
+ field("sort_order", list(field("item", binary(), true)), true),
590
+ field("cache_max_age_seconds", int64(), true),
591
+ field("start_position", binary(), true),
592
+ field("end_position", binary(), true),
593
+ ]);
594
+
374
595
  // Origin: method 'aggregate_bind' params
375
596
  export const AggregateBindParamsSchema = schema([
376
597
  field("request", binary(), false),
@@ -875,6 +1096,11 @@ export const TableFunctionDynamicToStringParamsSchema = schema([
875
1096
  field("request", binary(), false),
876
1097
  ]);
877
1098
 
1099
+ // Origin: method 'table_function_plan' params
1100
+ export const TableFunctionPlanParamsSchema = schema([
1101
+ field("request", binary(), false),
1102
+ ]);
1103
+
878
1104
  // Origin: method 'table_function_statistics' params
879
1105
  export const TableFunctionStatisticsParamsSchema = schema([
880
1106
  field("request", binary(), false),
package/src/http/fetch.ts CHANGED
@@ -10,10 +10,20 @@
10
10
  // `buildVgiProtocol` as `signingKey`, from one source. Passing different keys to
11
11
  // those two seams produces a worker that mints tokens it cannot recover.
12
12
 
13
- import { createHttpHandler, unpackStateToken, type Protocol } from "@query-farm/vgi-rpc";
13
+ import {
14
+ AuthContext,
15
+ createHttpHandler,
16
+ unpackStateToken,
17
+ type AuthenticateFn,
18
+ type DispatchHook,
19
+ type DispatchInfo,
20
+ type HookToken,
21
+ type Protocol,
22
+ } from "@query-farm/vgi-rpc";
14
23
  import { arrowStateSerializer } from "../protocol/state-serializer.js";
15
24
  import { buildVgiProtocol, type ProtocolConfig } from "../protocol/dispatch.js";
16
25
  import { createLandingRoutes, type LandingInfo } from "./landing.js";
26
+ import { setRequestAuthScope, type RequestAuthHolder } from "../request-auth.js";
17
27
 
18
28
  export interface VgiFetchOptions {
19
29
  /** Wire-protocol config (registry + catalogInterface). */
@@ -57,8 +67,100 @@ export interface VgiFetchOptions {
57
67
  * its required name/doc/version; the CF entry had to remember, and
58
68
  * vgi-open-meteo shipped for weeks without it. */
59
69
  landingInfo: LandingInfo;
70
+ /** Authenticates each incoming request, returning the caller's
71
+ * {@link AuthContext}. Forwarded verbatim to `createHttpHandler`; omit it
72
+ * and every caller is anonymous, which is what this package did before the
73
+ * option existed.
74
+ *
75
+ * Supplying it is what makes a worker able to serve more than one principal,
76
+ * and the identity it returns is the one the worker seals `attach_opaque_data`
77
+ * and split tokens under. */
78
+ authenticate?: AuthenticateFn;
79
+ }
80
+
81
+ // ---------------------------------------------------------------------------
82
+ // Request-scoped caller identity
83
+ //
84
+ // `@query-farm/vgi-rpc` types stream initialization as `(params) => state` —
85
+ // no CallContext — on both dispatchers, so the `init` handler cannot be handed
86
+ // the principal as an argument the way every unary handler is. But `init` is
87
+ // exactly where the identity-bound envelopes minted by `catalog_attach` and
88
+ // `table_function_plan` (both unary, both sealed under the REAL caller) are
89
+ // opened. Opening them under the anonymous identity works for anonymous
90
+ // callers and fails for everyone else.
91
+ //
92
+ // So the identity travels the way vgi-python's `current_auth()` ContextVar
93
+ // does: an ambient per-request scope. `AsyncLocalStorage.run` wraps the whole
94
+ // handler call, and a dispatch hook fills the holder in with whatever identity
95
+ // the framework actually dispatched under — the hook is read AFTER
96
+ // authentication, the PKCE cookie chain and bearer introspection have all had
97
+ // their say, so it reports the final principal rather than re-deriving one.
98
+ //
99
+ // A mutable holder rather than `enterWith`: `run` is the only form Cloudflare
100
+ // Workers supports, and the identity is not known until authentication has run
101
+ // inside the handler.
102
+ // ---------------------------------------------------------------------------
103
+
104
+ /** The `AsyncLocalStorage` surface used here, structurally typed so this file
105
+ * compiles where `node:async_hooks` has no types. */
106
+ interface AsyncLocalStorageLike<T> {
107
+ run<R>(store: T, fn: () => R): R;
108
+ getStore(): T | undefined;
60
109
  }
61
110
 
111
+ let requestAuthStorage: AsyncLocalStorageLike<RequestAuthHolder> | null | undefined;
112
+ let requestAuthStoragePending: Promise<AsyncLocalStorageLike<RequestAuthHolder> | null> | undefined;
113
+
114
+ /**
115
+ * Resolve (once) the AsyncLocalStorage this process scopes requests with.
116
+ *
117
+ * `node:async_hooks` is loaded dynamically and defensively: it exists on Node
118
+ * and Bun, and on Cloudflare Workers only with the `nodejs_compat` /
119
+ * `nodejs_als` compatibility flag. Where it is missing this resolves to `null`
120
+ * and the worker keeps serving exactly as it did before — anonymous callers
121
+ * unaffected, authenticated ones still unable to open their sealed attach.
122
+ * Degrading is the right failure here: refusing to start would break every
123
+ * anonymous deployment to fix a case it does not have.
124
+ */
125
+ function ensureRequestAuthStorage(): Promise<AsyncLocalStorageLike<RequestAuthHolder> | null> {
126
+ if (requestAuthStorage !== undefined) return Promise.resolve(requestAuthStorage);
127
+ requestAuthStoragePending ??= import("node:async_hooks")
128
+ .then((mod) => {
129
+ const storage = new mod.AsyncLocalStorage<RequestAuthHolder>() as AsyncLocalStorageLike<RequestAuthHolder>;
130
+ requestAuthStorage = storage;
131
+ setRequestAuthScope(storage);
132
+ return storage;
133
+ })
134
+ .catch(() => {
135
+ requestAuthStorage = null;
136
+ return null;
137
+ });
138
+ return requestAuthStoragePending;
139
+ }
140
+
141
+ /**
142
+ * Dispatch hook that records the dispatching principal on the open scope.
143
+ *
144
+ * Reads the identity off `DispatchInfo`, which the framework populates from the
145
+ * resolved auth context for every method — so this sees the same principal the
146
+ * access log and the unary handlers see, without this package having to know
147
+ * how authentication was configured.
148
+ */
149
+ const REQUEST_AUTH_DISPATCH_HOOK: DispatchHook = {
150
+ onDispatchStart(info: DispatchInfo): HookToken {
151
+ const holder = requestAuthStorage?.getStore();
152
+ if (holder) {
153
+ holder.auth = info.authenticated
154
+ ? new AuthContext(info.authDomain ?? "", true, info.principal ?? "", info.claims ?? {})
155
+ : AuthContext.anonymous();
156
+ }
157
+ return undefined;
158
+ },
159
+ onDispatchEnd() {
160
+ // Nothing to record; the scope ends when the request does.
161
+ },
162
+ };
163
+
62
164
  /**
63
165
  * Build a fetch handler suitable for `export default { fetch }` in a CF
64
166
  * Worker module, or for `Bun.serve({ fetch })`. The returned handler is
@@ -100,6 +202,11 @@ export function createVgiFetch(opts: VgiFetchOptions): (req: Request) => Promise
100
202
  tokenKey: opts.signingKey,
101
203
  tokenTtl,
102
204
  stateSerializer: arrowStateSerializer,
205
+ // Records the dispatching principal on the request scope opened below, so
206
+ // stream `init` — the one handler the framework passes no CallContext to —
207
+ // can still open the identity-bound attach and split envelopes.
208
+ dispatchHook: REQUEST_AUTH_DISPATCH_HOOK,
209
+ authenticate: opts.authenticate,
103
210
  // `null` is the explicit opt-out; `undefined` (omitted) means "*".
104
211
  corsOrigins: opts.corsOrigins === null ? undefined : (opts.corsOrigins ?? "*"),
105
212
  repositoryUrl: opts.repositoryUrl,
@@ -110,5 +217,13 @@ export function createVgiFetch(opts: VgiFetchOptions): (req: Request) => Promise
110
217
  extraRoutes: createLandingRoutes(opts.landingInfo),
111
218
  enableLandingPage: false,
112
219
  });
113
- return async (req: Request) => handler(req);
220
+ return async (req: Request) => {
221
+ const storage = await ensureRequestAuthStorage();
222
+ // No AsyncLocalStorage on this host: serve exactly as before.
223
+ if (!storage) return handler(req);
224
+ // One holder per request. The dispatch hook fills it in once the framework
225
+ // has resolved the principal; `init` reads it back through
226
+ // `currentRequestAuth()`.
227
+ return storage.run({}, () => handler(req));
228
+ };
114
229
  }
package/src/index.core.ts CHANGED
@@ -77,7 +77,7 @@ export {
77
77
  export type { VgiFunction, FunctionMeta, StreamHandlers, FunctionExample, HandlerState } from "./functions/types.js";
78
78
 
79
79
  // Catalog
80
- export { CatalogInterface, type CatalogAttachResult, type SchemaInfo, encodeSchemaInfo, decodeSchemaInfo, type TableInfo, encodeTableInfo, decodeTableInfo, type ViewInfo, encodeViewInfo, decodeViewInfo, type CatalogInfo, encodeCatalogInfo, decodeCatalogInfo, type FunctionInfo, encodeFunctionInfo, decodeFunctionInfo, type FunctionInfoOptions, type MacroInfo, encodeMacroInfo, decodeMacroInfo, type MacroType, type CopyFromFormatInfo, encodeCopyFromFormatInfo, type AttachOpaqueData, type TransactionOpaqueData, buildScanBranchesResult, type ScanBranchInput } from "./catalog/interface.js";
80
+ export { CatalogInterface, type CatalogAttachResult, type SchemaInfo, encodeSchemaInfo, decodeSchemaInfo, type TableInfo, encodeTableInfo, decodeTableInfo, type ViewInfo, encodeViewInfo, decodeViewInfo, type CatalogInfo, encodeCatalogInfo, decodeCatalogInfo, type FunctionInfo, encodeFunctionInfo, decodeFunctionInfo, type FunctionInfoOptions, type MacroInfo, encodeMacroInfo, decodeMacroInfo, type MacroType, type CopyFromFormatInfo, encodeCopyFromFormatInfo, type AttachOpaqueData, type TransactionOpaqueData, buildScanBranchesResult, encodeFormatOptions, type ScanBranchInput } from "./catalog/interface.js";
81
81
  export type { CatalogDescriptor, SchemaDescriptor, TableDescriptor, ViewDescriptor, MacroDescriptor, SettingDescriptor, SecretTypeDescriptor, ForeignKeyDef, DefaultValue } from "./catalog/descriptors.js";
82
82
  export { ReadOnlyCatalogInterface } from "./catalog/read-only.js";
83
83
  export { CompositeCatalogInterface } from "./catalog/composite.js";
@@ -219,7 +219,7 @@ export {
219
219
  int8, int16, int64,
220
220
  uint8, uint16, uint32, uint64,
221
221
  float16, float64,
222
- utf8, binary, fixedSizeBinary,
222
+ utf8, largeUtf8, binary, largeBinary, fixedSizeBinary,
223
223
  decimal, decimal128, decimal256,
224
224
  date, dateDay, dateMillisecond,
225
225
  time, timeSecond, timeMillisecond, timeMicrosecond, timeNanosecond,
@@ -20,8 +20,13 @@ import { deserializeArguments } from "../serialize.js";
20
20
  import { deserializeSchema, serializeSchema, batchToSecretDict } from "../../util/arrow/index.js";
21
21
  import { toUint8Array } from "../../util/bytes.js";
22
22
  import {
23
+ AggregateBindParamsSchema,
23
24
  AggregateBindResultSchema,
25
+ AggregateCombineParamsSchema,
26
+ AggregateDestructorParamsSchema,
27
+ AggregateFinalizeParamsSchema,
24
28
  AggregateFinalizeResultSchema,
29
+ AggregateUpdateParamsSchema,
25
30
  } from "../../generated/vgi-protocol-schemas.js";
26
31
  import {
27
32
  getExecutionState,
@@ -65,7 +70,7 @@ export function registerAggregateMethods(protocol: Protocol, registry: FunctionR
65
70
  // aggregate_bind — allocate execution_id, return output schema
66
71
  // ------------------------------------------------------------------------
67
72
  protocol.unary("aggregate_bind", {
68
- params: REQUEST_PARAMS_SCHEMA,
73
+ params: AggregateBindParamsSchema,
69
74
  result: RESULT_BINARY_SCHEMA,
70
75
  handler: async (params) => {
71
76
  const innerParams = unwrapRequest(params.request);
@@ -115,7 +120,7 @@ export function registerAggregateMethods(protocol: Protocol, registry: FunctionR
115
120
  // aggregate_update — fold the incoming batch into per-group state
116
121
  // ------------------------------------------------------------------------
117
122
  protocol.unary("aggregate_update", {
118
- params: REQUEST_PARAMS_SCHEMA,
123
+ params: AggregateUpdateParamsSchema,
119
124
  result: RESULT_BINARY_SCHEMA,
120
125
  handler: (params) => {
121
126
  const innerParams = unwrapRequest(params.request);
@@ -182,7 +187,7 @@ export function registerAggregateMethods(protocol: Protocol, registry: FunctionR
182
187
  // aggregate_combine — merge (source_group_id, target_group_id) pairs
183
188
  // ------------------------------------------------------------------------
184
189
  protocol.unary("aggregate_combine", {
185
- params: REQUEST_PARAMS_SCHEMA,
190
+ params: AggregateCombineParamsSchema,
186
191
  result: RESULT_BINARY_SCHEMA,
187
192
  handler: (params) => {
188
193
  const innerParams = unwrapRequest(params.request);
@@ -231,7 +236,7 @@ export function registerAggregateMethods(protocol: Protocol, registry: FunctionR
231
236
  // aggregate_finalize — emit one result row per requested group_id
232
237
  // ------------------------------------------------------------------------
233
238
  protocol.unary("aggregate_finalize", {
234
- params: REQUEST_PARAMS_SCHEMA,
239
+ params: AggregateFinalizeParamsSchema,
235
240
  result: RESULT_BINARY_SCHEMA,
236
241
  handler: (params) => {
237
242
  const innerParams = unwrapRequest(params.request);
@@ -281,7 +286,7 @@ export function registerAggregateMethods(protocol: Protocol, registry: FunctionR
281
286
  // up when the worker process exits).
282
287
  // ------------------------------------------------------------------------
283
288
  protocol.unary("aggregate_destructor", {
284
- params: REQUEST_PARAMS_SCHEMA,
289
+ params: AggregateDestructorParamsSchema,
285
290
  result: RESULT_BINARY_SCHEMA,
286
291
  handler: (params) => {
287
292
  const innerParams = unwrapRequest(params.request);