@query-farm/vgi-rpc 0.3.4 → 0.6.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 (87) hide show
  1. package/README.md +47 -0
  2. package/dist/auth.d.ts +13 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/client/connect.d.ts.map +1 -1
  5. package/dist/client/index.d.ts +2 -0
  6. package/dist/client/index.d.ts.map +1 -1
  7. package/dist/client/introspect.d.ts +1 -0
  8. package/dist/client/introspect.d.ts.map +1 -1
  9. package/dist/client/oauth.d.ts +62 -0
  10. package/dist/client/oauth.d.ts.map +1 -0
  11. package/dist/client/pipe.d.ts +3 -0
  12. package/dist/client/pipe.d.ts.map +1 -1
  13. package/dist/client/stream.d.ts +5 -0
  14. package/dist/client/stream.d.ts.map +1 -1
  15. package/dist/client/types.d.ts +6 -0
  16. package/dist/client/types.d.ts.map +1 -1
  17. package/dist/constants.d.ts +3 -1
  18. package/dist/constants.d.ts.map +1 -1
  19. package/dist/dispatch/describe.d.ts.map +1 -1
  20. package/dist/dispatch/stream.d.ts +2 -1
  21. package/dist/dispatch/stream.d.ts.map +1 -1
  22. package/dist/dispatch/unary.d.ts +2 -1
  23. package/dist/dispatch/unary.d.ts.map +1 -1
  24. package/dist/external.d.ts +45 -0
  25. package/dist/external.d.ts.map +1 -0
  26. package/dist/gcs.d.ts +38 -0
  27. package/dist/gcs.d.ts.map +1 -0
  28. package/dist/http/auth.d.ts +32 -0
  29. package/dist/http/auth.d.ts.map +1 -0
  30. package/dist/http/bearer.d.ts +34 -0
  31. package/dist/http/bearer.d.ts.map +1 -0
  32. package/dist/http/dispatch.d.ts +4 -0
  33. package/dist/http/dispatch.d.ts.map +1 -1
  34. package/dist/http/handler.d.ts.map +1 -1
  35. package/dist/http/index.d.ts +8 -0
  36. package/dist/http/index.d.ts.map +1 -1
  37. package/dist/http/jwt.d.ts +21 -0
  38. package/dist/http/jwt.d.ts.map +1 -0
  39. package/dist/http/mtls.d.ts +78 -0
  40. package/dist/http/mtls.d.ts.map +1 -0
  41. package/dist/http/pages.d.ts +9 -0
  42. package/dist/http/pages.d.ts.map +1 -0
  43. package/dist/http/types.d.ts +22 -1
  44. package/dist/http/types.d.ts.map +1 -1
  45. package/dist/index.d.ts +4 -2
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +2576 -317
  48. package/dist/index.js.map +27 -18
  49. package/dist/otel.d.ts +47 -0
  50. package/dist/otel.d.ts.map +1 -0
  51. package/dist/s3.d.ts +43 -0
  52. package/dist/s3.d.ts.map +1 -0
  53. package/dist/server.d.ts +6 -0
  54. package/dist/server.d.ts.map +1 -1
  55. package/dist/types.d.ts +38 -2
  56. package/dist/types.d.ts.map +1 -1
  57. package/dist/wire/response.d.ts.map +1 -1
  58. package/package.json +46 -2
  59. package/src/auth.ts +31 -0
  60. package/src/client/connect.ts +28 -6
  61. package/src/client/index.ts +11 -0
  62. package/src/client/introspect.ts +15 -3
  63. package/src/client/oauth.ts +167 -0
  64. package/src/client/pipe.ts +19 -4
  65. package/src/client/stream.ts +32 -7
  66. package/src/client/types.ts +6 -0
  67. package/src/constants.ts +4 -1
  68. package/src/dispatch/describe.ts +20 -0
  69. package/src/dispatch/stream.ts +18 -4
  70. package/src/dispatch/unary.ts +6 -1
  71. package/src/external.ts +209 -0
  72. package/src/gcs.ts +86 -0
  73. package/src/http/auth.ts +110 -0
  74. package/src/http/bearer.ts +107 -0
  75. package/src/http/dispatch.ts +32 -10
  76. package/src/http/handler.ts +120 -3
  77. package/src/http/index.ts +14 -0
  78. package/src/http/jwt.ts +80 -0
  79. package/src/http/mtls.ts +298 -0
  80. package/src/http/pages.ts +298 -0
  81. package/src/http/types.ts +23 -1
  82. package/src/index.ts +32 -0
  83. package/src/otel.ts +161 -0
  84. package/src/s3.ts +94 -0
  85. package/src/server.ts +42 -8
  86. package/src/types.ts +51 -3
  87. package/src/wire/response.ts +28 -14
package/dist/index.js CHANGED
@@ -50,6 +50,48 @@ var init_zstd = __esm(() => {
50
50
  isBun = typeof globalThis.Bun !== "undefined";
51
51
  });
52
52
 
53
+ // src/errors.ts
54
+ class RpcError extends Error {
55
+ errorType;
56
+ errorMessage;
57
+ remoteTraceback;
58
+ constructor(errorType, errorMessage, remoteTraceback) {
59
+ super(`${errorType}: ${errorMessage}`);
60
+ this.errorType = errorType;
61
+ this.errorMessage = errorMessage;
62
+ this.remoteTraceback = remoteTraceback;
63
+ this.name = "RpcError";
64
+ }
65
+ }
66
+
67
+ class VersionError extends Error {
68
+ constructor(message) {
69
+ super(message);
70
+ this.name = "VersionError";
71
+ }
72
+ }
73
+
74
+ // src/auth.ts
75
+ class AuthContext {
76
+ domain;
77
+ authenticated;
78
+ principal;
79
+ claims;
80
+ constructor(domain, authenticated, principal, claims = {}) {
81
+ this.domain = domain;
82
+ this.authenticated = authenticated;
83
+ this.principal = principal;
84
+ this.claims = claims;
85
+ }
86
+ static anonymous() {
87
+ return new AuthContext("", false, null);
88
+ }
89
+ requireAuthenticated() {
90
+ if (!this.authenticated) {
91
+ throw new RpcError("AuthenticationError", "Authentication required", "");
92
+ }
93
+ }
94
+ }
53
95
  // src/constants.ts
54
96
  var RPC_METHOD_KEY = "vgi_rpc.method";
55
97
  var LOG_LEVEL_KEY = "vgi_rpc.log_level";
@@ -61,21 +103,247 @@ var SERVER_ID_KEY = "vgi_rpc.server_id";
61
103
  var REQUEST_ID_KEY = "vgi_rpc.request_id";
62
104
  var PROTOCOL_NAME_KEY = "vgi_rpc.protocol_name";
63
105
  var DESCRIBE_VERSION_KEY = "vgi_rpc.describe_version";
64
- var DESCRIBE_VERSION = "2";
106
+ var DESCRIBE_VERSION = "3";
65
107
  var DESCRIBE_METHOD_NAME = "__describe__";
66
108
  var STATE_KEY = "vgi_rpc.stream_state#b64";
109
+ var LOCATION_KEY = "vgi_rpc.location";
110
+ var LOCATION_SHA256_KEY = "vgi_rpc.location.sha256";
67
111
 
68
- // src/http/common.ts
112
+ // src/external.ts
69
113
  import { RecordBatchReader, RecordBatchStreamWriter } from "@query-farm/apache-arrow";
114
+ init_zstd();
70
115
 
71
- // src/util/conform.ts
116
+ // src/wire/response.ts
72
117
  import {
118
+ Data,
119
+ DataType,
73
120
  makeData,
74
121
  RecordBatch,
75
122
  Struct,
76
- Type,
77
123
  vectorFromArray
78
124
  } from "@query-farm/apache-arrow";
125
+ function coerceInt64(schema, values) {
126
+ const result = { ...values };
127
+ for (const field of schema.fields) {
128
+ const val = result[field.name];
129
+ if (val === undefined)
130
+ continue;
131
+ if (!DataType.isInt(field.type) || field.type.bitWidth !== 64)
132
+ continue;
133
+ if (Array.isArray(val)) {
134
+ result[field.name] = val.map((v) => typeof v === "number" ? BigInt(v) : v);
135
+ } else if (typeof val === "number") {
136
+ result[field.name] = BigInt(val);
137
+ }
138
+ }
139
+ return result;
140
+ }
141
+ function buildResultBatch(schema, values, serverId, requestId) {
142
+ const metadata = new Map;
143
+ metadata.set(SERVER_ID_KEY, serverId);
144
+ if (requestId !== null) {
145
+ metadata.set(REQUEST_ID_KEY, requestId);
146
+ }
147
+ if (schema.fields.length === 0) {
148
+ return buildEmptyBatch(schema, metadata);
149
+ }
150
+ for (const field of schema.fields) {
151
+ if (values[field.name] === undefined && !field.nullable) {
152
+ const got = Object.keys(values);
153
+ throw new TypeError(`Handler result missing required field '${field.name}'. Got keys: [${got.join(", ")}]`);
154
+ }
155
+ }
156
+ const coerced = coerceInt64(schema, values);
157
+ const children = schema.fields.map((f) => {
158
+ const val = coerced[f.name];
159
+ if (val instanceof Data) {
160
+ return val;
161
+ }
162
+ const arr = vectorFromArray([val], f.type);
163
+ return arr.data[0];
164
+ });
165
+ const structType = new Struct(schema.fields);
166
+ const data = makeData({
167
+ type: structType,
168
+ length: 1,
169
+ children,
170
+ nullCount: 0
171
+ });
172
+ return new RecordBatch(schema, data, metadata);
173
+ }
174
+ function buildErrorBatch(schema, error, serverId, requestId) {
175
+ const metadata = new Map;
176
+ metadata.set(LOG_LEVEL_KEY, "EXCEPTION");
177
+ metadata.set(LOG_MESSAGE_KEY, `${error.constructor.name}: ${error.message}`);
178
+ const extra = {
179
+ exception_type: error.constructor.name,
180
+ exception_message: error.message,
181
+ traceback: error.stack ?? ""
182
+ };
183
+ metadata.set(LOG_EXTRA_KEY, JSON.stringify(extra));
184
+ metadata.set(SERVER_ID_KEY, serverId);
185
+ if (requestId !== null) {
186
+ metadata.set(REQUEST_ID_KEY, requestId);
187
+ }
188
+ return buildEmptyBatch(schema, metadata);
189
+ }
190
+ function buildLogBatch(schema, level, message, extra, serverId, requestId) {
191
+ const metadata = new Map;
192
+ metadata.set(LOG_LEVEL_KEY, level);
193
+ metadata.set(LOG_MESSAGE_KEY, message);
194
+ if (extra) {
195
+ metadata.set(LOG_EXTRA_KEY, JSON.stringify(extra));
196
+ }
197
+ if (serverId != null) {
198
+ metadata.set(SERVER_ID_KEY, serverId);
199
+ }
200
+ if (requestId != null) {
201
+ metadata.set(REQUEST_ID_KEY, requestId);
202
+ }
203
+ return buildEmptyBatch(schema, metadata);
204
+ }
205
+ function makeEmptyData(type) {
206
+ if (DataType.isStruct(type)) {
207
+ const children = type.children.map((f) => makeEmptyData(f.type));
208
+ return makeData({ type, length: 0, children, nullCount: 0 });
209
+ }
210
+ if (DataType.isList(type)) {
211
+ const childData = makeEmptyData(type.children[0].type);
212
+ return makeData({ type, length: 0, children: [childData], nullCount: 0, valueOffsets: new Int32Array([0]) });
213
+ }
214
+ if (DataType.isFixedSizeList(type)) {
215
+ const childData = makeEmptyData(type.children[0].type);
216
+ return makeData({ type, length: 0, child: childData, nullCount: 0 });
217
+ }
218
+ if (DataType.isMap(type)) {
219
+ const entryType = type.children[0]?.type;
220
+ const entryData = entryType ? makeEmptyData(entryType) : makeData({ type: new Struct([]), length: 0, children: [], nullCount: 0 });
221
+ return makeData({ type, length: 0, children: [entryData], nullCount: 0, valueOffsets: new Int32Array([0]) });
222
+ }
223
+ return makeData({ type, length: 0, nullCount: 0 });
224
+ }
225
+ function buildEmptyBatch(schema, metadata) {
226
+ const children = schema.fields.map((f) => makeEmptyData(f.type));
227
+ const structType = new Struct(schema.fields);
228
+ const data = makeData({
229
+ type: structType,
230
+ length: 0,
231
+ children,
232
+ nullCount: 0
233
+ });
234
+ return new RecordBatch(schema, data, metadata);
235
+ }
236
+
237
+ // src/external.ts
238
+ var DEFAULT_THRESHOLD = 1048576;
239
+ function httpsOnlyValidator(url) {
240
+ const parsed = new URL(url);
241
+ if (parsed.protocol !== "https:") {
242
+ throw new Error(`External location URL must use HTTPS, got "${parsed.protocol}"`);
243
+ }
244
+ }
245
+ async function sha256Hex(data) {
246
+ const buf = new ArrayBuffer(data.byteLength);
247
+ new Uint8Array(buf).set(data);
248
+ const hash = await crypto.subtle.digest("SHA-256", buf);
249
+ return Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("");
250
+ }
251
+ function isExternalLocationBatch(batch) {
252
+ if (batch.numRows !== 0)
253
+ return false;
254
+ const meta = batch.metadata;
255
+ if (!meta)
256
+ return false;
257
+ return meta.has(LOCATION_KEY) && !meta.has(LOG_LEVEL_KEY);
258
+ }
259
+ function makeExternalLocationBatch(schema, url, sha256) {
260
+ const metadata = new Map;
261
+ metadata.set(LOCATION_KEY, url);
262
+ if (sha256) {
263
+ metadata.set(LOCATION_SHA256_KEY, sha256);
264
+ }
265
+ return buildEmptyBatch(schema, metadata);
266
+ }
267
+ function serializeBatchToIpc(batch) {
268
+ const writer = new RecordBatchStreamWriter;
269
+ writer.reset(undefined, batch.schema);
270
+ writer.write(batch);
271
+ writer.close();
272
+ return writer.toUint8Array(true);
273
+ }
274
+ function batchByteSize(batch) {
275
+ const writer = new RecordBatchStreamWriter;
276
+ writer.reset(undefined, batch.schema);
277
+ writer.write(batch);
278
+ writer.close();
279
+ return writer.toUint8Array(true).byteLength;
280
+ }
281
+ async function maybeExternalizeBatch(batch, config) {
282
+ if (!config?.storage)
283
+ return batch;
284
+ if (batch.numRows === 0)
285
+ return batch;
286
+ const threshold = config.externalizeThresholdBytes ?? DEFAULT_THRESHOLD;
287
+ if (batchByteSize(batch) < threshold)
288
+ return batch;
289
+ let ipcData = serializeBatchToIpc(batch);
290
+ const checksum = await sha256Hex(ipcData);
291
+ let contentEncoding = "";
292
+ if (config.compression?.algorithm === "zstd") {
293
+ ipcData = zstdCompress(ipcData, config.compression.level ?? 3);
294
+ contentEncoding = "zstd";
295
+ }
296
+ const url = await config.storage.upload(ipcData, contentEncoding);
297
+ return makeExternalLocationBatch(batch.schema, url, checksum);
298
+ }
299
+ async function resolveExternalLocation(batch, config) {
300
+ if (!config)
301
+ return batch;
302
+ if (!isExternalLocationBatch(batch))
303
+ return batch;
304
+ const url = batch.metadata?.get(LOCATION_KEY);
305
+ if (!url)
306
+ return batch;
307
+ const validator = config.urlValidator === null ? undefined : config.urlValidator ?? httpsOnlyValidator;
308
+ if (validator) {
309
+ validator(url);
310
+ }
311
+ const response = await fetch(url);
312
+ if (!response.ok) {
313
+ throw new Error(`External location fetch failed: ${response.status} ${response.statusText} [url: ${url}]`);
314
+ }
315
+ let data = new Uint8Array(await response.arrayBuffer());
316
+ const contentEncoding = response.headers.get("Content-Encoding");
317
+ if (contentEncoding === "zstd") {
318
+ data = new Uint8Array(zstdDecompress(data));
319
+ }
320
+ const expectedSha256 = batch.metadata?.get(LOCATION_SHA256_KEY);
321
+ if (expectedSha256) {
322
+ const actualSha256 = await sha256Hex(data);
323
+ if (actualSha256 !== expectedSha256) {
324
+ throw new Error(`SHA-256 checksum mismatch for ${url}: expected ${expectedSha256}, got ${actualSha256}`);
325
+ }
326
+ }
327
+ const reader = await RecordBatchReader.from(data);
328
+ await reader.open();
329
+ const resolved = reader.next();
330
+ if (!resolved || resolved.done || !resolved.value) {
331
+ throw new Error(`No data batch found in external IPC stream from ${url}`);
332
+ }
333
+ return resolved.value;
334
+ }
335
+
336
+ // src/http/common.ts
337
+ import { RecordBatchReader as RecordBatchReader2, RecordBatchStreamWriter as RecordBatchStreamWriter2 } from "@query-farm/apache-arrow";
338
+
339
+ // src/util/conform.ts
340
+ import {
341
+ makeData as makeData2,
342
+ RecordBatch as RecordBatch2,
343
+ Struct as Struct2,
344
+ Type,
345
+ vectorFromArray as vectorFromArray2
346
+ } from "@query-farm/apache-arrow";
79
347
  function needsValueCast(src, dst) {
80
348
  if (src.typeId === dst.typeId)
81
349
  return false;
@@ -111,19 +379,19 @@ function conformBatchToSchema(batch, schema) {
111
379
  const v = col.get(r);
112
380
  values.push(typeof v === "bigint" ? Number(v) : v);
113
381
  }
114
- return vectorFromArray(values, dstType).data[0];
382
+ return vectorFromArray2(values, dstType).data[0];
115
383
  }
116
384
  return srcChild.clone(dstType);
117
385
  });
118
- const structType = new Struct(schema.fields);
119
- const data = makeData({
386
+ const structType = new Struct2(schema.fields);
387
+ const data = makeData2({
120
388
  type: structType,
121
389
  length: batch.numRows,
122
390
  children,
123
391
  nullCount: batch.data.nullCount,
124
392
  nullBitmap: batch.data.nullBitmap
125
393
  });
126
- return new RecordBatch(schema, data, batch.metadata);
394
+ return new RecordBatch2(schema, data, batch.metadata);
127
395
  }
128
396
 
129
397
  // src/http/common.ts
@@ -138,7 +406,7 @@ class HttpRpcError extends Error {
138
406
  }
139
407
  }
140
408
  function serializeIpcStream(schema, batches) {
141
- const writer = new RecordBatchStreamWriter;
409
+ const writer = new RecordBatchStreamWriter2;
142
410
  writer.reset(undefined, schema);
143
411
  for (const batch of batches) {
144
412
  writer.write(conformBatchToSchema(batch, schema));
@@ -152,7 +420,7 @@ function arrowResponse(body, status = 200, extraHeaders) {
152
420
  return new Response(body, { status, headers });
153
421
  }
154
422
  async function readRequestFromBody(body) {
155
- const reader = await RecordBatchReader.from(body);
423
+ const reader = await RecordBatchReader2.from(body);
156
424
  await reader.open();
157
425
  const schema = reader.schema;
158
426
  if (!schema) {
@@ -166,46 +434,25 @@ async function readRequestFromBody(body) {
166
434
  }
167
435
 
168
436
  // src/client/introspect.ts
169
- import { Schema as ArrowSchema, RecordBatchReader as RecordBatchReader4 } from "@query-farm/apache-arrow";
437
+ import { Schema as ArrowSchema, RecordBatchReader as RecordBatchReader5 } from "@query-farm/apache-arrow";
170
438
 
171
439
  // src/client/ipc.ts
172
440
  import {
173
441
  Binary,
174
442
  Bool,
175
- DataType,
443
+ DataType as DataType2,
176
444
  Float64,
177
445
  Int64,
178
- makeData as makeData2,
179
- RecordBatch as RecordBatch2,
180
- RecordBatchReader as RecordBatchReader3,
181
- Struct as Struct2,
446
+ makeData as makeData3,
447
+ RecordBatch as RecordBatch3,
448
+ RecordBatchReader as RecordBatchReader4,
449
+ Struct as Struct3,
182
450
  Utf8,
183
- vectorFromArray as vectorFromArray2
451
+ vectorFromArray as vectorFromArray3
184
452
  } from "@query-farm/apache-arrow";
185
453
 
186
- // src/errors.ts
187
- class RpcError extends Error {
188
- errorType;
189
- errorMessage;
190
- remoteTraceback;
191
- constructor(errorType, errorMessage, remoteTraceback) {
192
- super(`${errorType}: ${errorMessage}`);
193
- this.errorType = errorType;
194
- this.errorMessage = errorMessage;
195
- this.remoteTraceback = remoteTraceback;
196
- this.name = "RpcError";
197
- }
198
- }
199
-
200
- class VersionError extends Error {
201
- constructor(message) {
202
- super(message);
203
- this.name = "VersionError";
204
- }
205
- }
206
-
207
454
  // src/wire/reader.ts
208
- import { RecordBatchReader as RecordBatchReader2 } from "@query-farm/apache-arrow";
455
+ import { RecordBatchReader as RecordBatchReader3 } from "@query-farm/apache-arrow";
209
456
 
210
457
  class IpcStreamReader {
211
458
  reader;
@@ -215,7 +462,7 @@ class IpcStreamReader {
215
462
  this.reader = reader;
216
463
  }
217
464
  static async create(input) {
218
- const reader = await RecordBatchReader2.from(input);
465
+ const reader = await RecordBatchReader3.from(input);
219
466
  await reader.open({ autoDestroy: false });
220
467
  if (reader.closed) {
221
468
  throw new Error("Input stream closed before first IPC message");
@@ -292,12 +539,12 @@ function inferArrowType(value) {
292
539
  function coerceForArrow(type, value) {
293
540
  if (value == null)
294
541
  return value;
295
- if (DataType.isInt(type) && type.bitWidth === 64) {
542
+ if (DataType2.isInt(type) && type.bitWidth === 64) {
296
543
  if (typeof value === "number")
297
544
  return BigInt(value);
298
545
  return value;
299
546
  }
300
- if (DataType.isMap(type)) {
547
+ if (DataType2.isMap(type)) {
301
548
  if (value instanceof Map) {
302
549
  const entriesField = type.children[0];
303
550
  const valueType = entriesField.type.children[1].type;
@@ -309,7 +556,7 @@ function coerceForArrow(type, value) {
309
556
  }
310
557
  return value;
311
558
  }
312
- if (DataType.isList(type)) {
559
+ if (DataType2.isList(type)) {
313
560
  if (Array.isArray(value)) {
314
561
  const elemType = type.children[0].type;
315
562
  return value.map((v) => coerceForArrow(elemType, v));
@@ -323,32 +570,32 @@ function buildRequestIpc(schema, params, method) {
323
570
  metadata.set(RPC_METHOD_KEY, method);
324
571
  metadata.set(REQUEST_VERSION_KEY, REQUEST_VERSION);
325
572
  if (schema.fields.length === 0) {
326
- const structType2 = new Struct2(schema.fields);
327
- const data2 = makeData2({
573
+ const structType2 = new Struct3(schema.fields);
574
+ const data2 = makeData3({
328
575
  type: structType2,
329
576
  length: 1,
330
577
  children: [],
331
578
  nullCount: 0
332
579
  });
333
- const batch2 = new RecordBatch2(schema, data2, metadata);
580
+ const batch2 = new RecordBatch3(schema, data2, metadata);
334
581
  return serializeIpcStream(schema, [batch2]);
335
582
  }
336
583
  const children = schema.fields.map((f) => {
337
584
  const val = coerceForArrow(f.type, params[f.name]);
338
- return vectorFromArray2([val], f.type).data[0];
585
+ return vectorFromArray3([val], f.type).data[0];
339
586
  });
340
- const structType = new Struct2(schema.fields);
341
- const data = makeData2({
587
+ const structType = new Struct3(schema.fields);
588
+ const data = makeData3({
342
589
  type: structType,
343
590
  length: 1,
344
591
  children,
345
592
  nullCount: 0
346
593
  });
347
- const batch = new RecordBatch2(schema, data, metadata);
594
+ const batch = new RecordBatch3(schema, data, metadata);
348
595
  return serializeIpcStream(schema, [batch]);
349
596
  }
350
597
  async function readResponseBatches(body) {
351
- const reader = await RecordBatchReader3.from(body);
598
+ const reader = await RecordBatchReader4.from(body);
352
599
  await reader.open();
353
600
  const schema = reader.schema;
354
601
  if (!schema) {
@@ -422,7 +669,7 @@ async function readSequentialStreams(body) {
422
669
 
423
670
  // src/client/introspect.ts
424
671
  async function deserializeSchema(bytes) {
425
- const reader = await RecordBatchReader4.from(bytes);
672
+ const reader = await RecordBatchReader5.from(bytes);
426
673
  await reader.open();
427
674
  return reader.schema;
428
675
  }
@@ -486,21 +733,28 @@ async function parseDescribeResponse(batches, onLog) {
486
733
  return { protocolName, methods };
487
734
  }
488
735
  async function httpIntrospect(baseUrl, options) {
489
- const prefix = options?.prefix ?? "/vgi";
736
+ const prefix = options?.prefix ?? "";
490
737
  const emptySchema = new ArrowSchema([]);
491
738
  const body = buildRequestIpc(emptySchema, {}, DESCRIBE_METHOD_NAME);
739
+ const headers = { "Content-Type": ARROW_CONTENT_TYPE };
740
+ if (options?.authorization) {
741
+ headers.Authorization = options.authorization;
742
+ }
492
743
  const response = await fetch(`${baseUrl}${prefix}/${DESCRIBE_METHOD_NAME}`, {
493
744
  method: "POST",
494
- headers: { "Content-Type": ARROW_CONTENT_TYPE },
745
+ headers,
495
746
  body
496
747
  });
748
+ if (response.status === 401) {
749
+ throw new RpcError("AuthenticationError", "Authentication required", "");
750
+ }
497
751
  const responseBody = new Uint8Array(await response.arrayBuffer());
498
752
  const { batches } = await readResponseBatches(responseBody);
499
753
  return parseDescribeResponse(batches);
500
754
  }
501
755
 
502
756
  // src/client/stream.ts
503
- import { Field, makeData as makeData3, RecordBatch as RecordBatch3, Schema, Struct as Struct3, vectorFromArray as vectorFromArray3 } from "@query-farm/apache-arrow";
757
+ import { Field, makeData as makeData4, RecordBatch as RecordBatch4, Schema, Struct as Struct4, vectorFromArray as vectorFromArray4 } from "@query-farm/apache-arrow";
504
758
  class HttpStreamSession {
505
759
  _baseUrl;
506
760
  _prefix;
@@ -515,6 +769,8 @@ class HttpStreamSession {
515
769
  _compressionLevel;
516
770
  _compressFn;
517
771
  _decompressFn;
772
+ _authorization;
773
+ _externalConfig;
518
774
  constructor(opts) {
519
775
  this._baseUrl = opts.baseUrl;
520
776
  this._prefix = opts.prefix;
@@ -529,6 +785,8 @@ class HttpStreamSession {
529
785
  this._compressionLevel = opts.compressionLevel;
530
786
  this._compressFn = opts.compressFn;
531
787
  this._decompressFn = opts.decompressFn;
788
+ this._authorization = opts.authorization;
789
+ this._externalConfig = opts.externalConfig;
532
790
  }
533
791
  get header() {
534
792
  return this._header;
@@ -541,6 +799,9 @@ class HttpStreamSession {
541
799
  headers["Content-Encoding"] = "zstd";
542
800
  headers["Accept-Encoding"] = "zstd";
543
801
  }
802
+ if (this._authorization) {
803
+ headers.Authorization = this._authorization;
804
+ }
544
805
  return headers;
545
806
  }
546
807
  _prepareBody(content) {
@@ -565,7 +826,7 @@ class HttpStreamSession {
565
826
  const emptyBatch = this._buildEmptyBatch(zeroSchema);
566
827
  const metadata2 = new Map;
567
828
  metadata2.set(STATE_KEY, this._stateToken);
568
- const batchWithMeta = new RecordBatch3(zeroSchema, emptyBatch.data, metadata2);
829
+ const batchWithMeta = new RecordBatch4(zeroSchema, emptyBatch.data, metadata2);
569
830
  return this._doExchange(zeroSchema, [batchWithMeta]);
570
831
  }
571
832
  const keys = Object.keys(input[0]);
@@ -584,10 +845,10 @@ class HttpStreamSession {
584
845
  const inputSchema = new Schema(fields);
585
846
  const children = inputSchema.fields.map((f) => {
586
847
  const values = input.map((row) => row[f.name]);
587
- return vectorFromArray3(values, f.type).data[0];
848
+ return vectorFromArray4(values, f.type).data[0];
588
849
  });
589
- const structType = new Struct3(inputSchema.fields);
590
- const data = makeData3({
850
+ const structType = new Struct4(inputSchema.fields);
851
+ const data = makeData4({
591
852
  type: structType,
592
853
  length: input.length,
593
854
  children,
@@ -595,7 +856,7 @@ class HttpStreamSession {
595
856
  });
596
857
  const metadata = new Map;
597
858
  metadata.set(STATE_KEY, this._stateToken);
598
- const batch = new RecordBatch3(inputSchema, data, metadata);
859
+ const batch = new RecordBatch4(inputSchema, data, metadata);
599
860
  return this._doExchange(inputSchema, [batch]);
600
861
  }
601
862
  async _doExchange(schema, batches) {
@@ -605,6 +866,9 @@ class HttpStreamSession {
605
866
  headers: this._buildHeaders(),
606
867
  body: this._prepareBody(body)
607
868
  });
869
+ if (resp.status === 401) {
870
+ throw new RpcError("AuthenticationError", "Authentication required", "");
871
+ }
608
872
  const responseBody = await this._readResponse(resp);
609
873
  const { batches: responseBatches } = await readResponseBatches(responseBody);
610
874
  let resultRows = [];
@@ -627,22 +891,26 @@ class HttpStreamSession {
627
891
  }
628
892
  _buildEmptyBatch(schema) {
629
893
  const children = schema.fields.map((f) => {
630
- return makeData3({ type: f.type, length: 0, nullCount: 0 });
894
+ return makeData4({ type: f.type, length: 0, nullCount: 0 });
631
895
  });
632
- const structType = new Struct3(schema.fields);
633
- const data = makeData3({
896
+ const structType = new Struct4(schema.fields);
897
+ const data = makeData4({
634
898
  type: structType,
635
899
  length: 0,
636
900
  children,
637
901
  nullCount: 0
638
902
  });
639
- return new RecordBatch3(schema, data);
903
+ return new RecordBatch4(schema, data);
640
904
  }
641
905
  async* [Symbol.asyncIterator]() {
642
- for (const batch of this._pendingBatches) {
906
+ for (let batch of this._pendingBatches) {
643
907
  if (batch.numRows === 0) {
644
- dispatchLogOrError(batch, this._onLog);
645
- continue;
908
+ if (isExternalLocationBatch(batch)) {
909
+ batch = await resolveExternalLocation(batch, this._externalConfig);
910
+ } else {
911
+ dispatchLogOrError(batch, this._onLog);
912
+ continue;
913
+ }
646
914
  }
647
915
  yield extractBatchRows(batch);
648
916
  }
@@ -655,7 +923,7 @@ class HttpStreamSession {
655
923
  const responseBody = await this._sendContinuation(this._stateToken);
656
924
  const { batches } = await readResponseBatches(responseBody);
657
925
  let gotContinuation = false;
658
- for (const batch of batches) {
926
+ for (let batch of batches) {
659
927
  if (batch.numRows === 0) {
660
928
  const token = batch.metadata?.get(STATE_KEY);
661
929
  if (token) {
@@ -663,8 +931,12 @@ class HttpStreamSession {
663
931
  gotContinuation = true;
664
932
  continue;
665
933
  }
666
- dispatchLogOrError(batch, this._onLog);
667
- continue;
934
+ if (isExternalLocationBatch(batch)) {
935
+ batch = await resolveExternalLocation(batch, this._externalConfig);
936
+ } else {
937
+ dispatchLogOrError(batch, this._onLog);
938
+ continue;
939
+ }
668
940
  }
669
941
  yield extractBatchRows(batch);
670
942
  }
@@ -676,20 +948,23 @@ class HttpStreamSession {
676
948
  const emptySchema = new Schema([]);
677
949
  const metadata = new Map;
678
950
  metadata.set(STATE_KEY, token);
679
- const structType = new Struct3(emptySchema.fields);
680
- const data = makeData3({
951
+ const structType = new Struct4(emptySchema.fields);
952
+ const data = makeData4({
681
953
  type: structType,
682
954
  length: 1,
683
955
  children: [],
684
956
  nullCount: 0
685
957
  });
686
- const batch = new RecordBatch3(emptySchema, data, metadata);
958
+ const batch = new RecordBatch4(emptySchema, data, metadata);
687
959
  const body = serializeIpcStream(emptySchema, [batch]);
688
960
  const resp = await fetch(`${this._baseUrl}${this._prefix}/${this._method}/exchange`, {
689
961
  method: "POST",
690
962
  headers: this._buildHeaders(),
691
963
  body: this._prepareBody(body)
692
964
  });
965
+ if (resp.status === 401) {
966
+ throw new RpcError("AuthenticationError", "Authentication required", "");
967
+ }
693
968
  return this._readResponse(resp);
694
969
  }
695
970
  close() {}
@@ -697,9 +972,11 @@ class HttpStreamSession {
697
972
 
698
973
  // src/client/connect.ts
699
974
  function httpConnect(baseUrl, options) {
700
- const prefix = (options?.prefix ?? "/vgi").replace(/\/+$/, "");
975
+ const prefix = (options?.prefix ?? "").replace(/\/+$/, "");
701
976
  const onLog = options?.onLog;
702
977
  const compressionLevel = options?.compressionLevel;
978
+ const authorization = options?.authorization;
979
+ const externalConfig = options?.externalLocation;
703
980
  let methodCache = null;
704
981
  let compressFn;
705
982
  let decompressFn;
@@ -722,6 +999,9 @@ function httpConnect(baseUrl, options) {
722
999
  headers["Content-Encoding"] = "zstd";
723
1000
  headers["Accept-Encoding"] = "zstd";
724
1001
  }
1002
+ if (authorization) {
1003
+ headers.Authorization = authorization;
1004
+ }
725
1005
  return headers;
726
1006
  }
727
1007
  function prepareBody(content) {
@@ -730,6 +1010,11 @@ function httpConnect(baseUrl, options) {
730
1010
  }
731
1011
  return content;
732
1012
  }
1013
+ function checkAuth(resp) {
1014
+ if (resp.status === 401) {
1015
+ throw new RpcError("AuthenticationError", "Authentication required", "");
1016
+ }
1017
+ }
733
1018
  async function readResponse(resp) {
734
1019
  let body = new Uint8Array(await resp.arrayBuffer());
735
1020
  if (resp.headers.get("Content-Encoding") === "zstd" && decompressFn) {
@@ -740,7 +1025,7 @@ function httpConnect(baseUrl, options) {
740
1025
  async function ensureMethodCache() {
741
1026
  if (methodCache)
742
1027
  return methodCache;
743
- const desc = await httpIntrospect(baseUrl, { prefix });
1028
+ const desc = await httpIntrospect(baseUrl, { prefix, authorization });
744
1029
  methodCache = new Map(desc.methods.map((m) => [m.name, m]));
745
1030
  return methodCache;
746
1031
  }
@@ -759,13 +1044,18 @@ function httpConnect(baseUrl, options) {
759
1044
  headers: buildHeaders(),
760
1045
  body: prepareBody(body)
761
1046
  });
1047
+ checkAuth(resp);
762
1048
  const responseBody = await readResponse(resp);
763
1049
  const { batches } = await readResponseBatches(responseBody);
764
1050
  let resultBatch = null;
765
- for (const batch of batches) {
1051
+ for (let batch of batches) {
766
1052
  if (batch.numRows === 0) {
767
- dispatchLogOrError(batch, onLog);
768
- continue;
1053
+ if (isExternalLocationBatch(batch)) {
1054
+ batch = await resolveExternalLocation(batch, externalConfig);
1055
+ } else {
1056
+ dispatchLogOrError(batch, onLog);
1057
+ continue;
1058
+ }
769
1059
  }
770
1060
  resultBatch = batch;
771
1061
  }
@@ -794,6 +1084,7 @@ function httpConnect(baseUrl, options) {
794
1084
  headers: buildHeaders(),
795
1085
  body: prepareBody(body)
796
1086
  });
1087
+ checkAuth(resp);
797
1088
  const responseBody = await readResponse(resp);
798
1089
  let header = null;
799
1090
  let stateToken = null;
@@ -899,7 +1190,9 @@ function httpConnect(baseUrl, options) {
899
1190
  header,
900
1191
  compressionLevel,
901
1192
  compressFn,
902
- decompressFn
1193
+ decompressFn,
1194
+ authorization,
1195
+ externalConfig
903
1196
  });
904
1197
  },
905
1198
  async describe() {
@@ -908,15 +1201,124 @@ function httpConnect(baseUrl, options) {
908
1201
  close() {}
909
1202
  };
910
1203
  }
1204
+ // src/client/oauth.ts
1205
+ function parseMetadataJson(json) {
1206
+ const result = {
1207
+ resource: json.resource,
1208
+ authorizationServers: json.authorization_servers
1209
+ };
1210
+ if (json.scopes_supported)
1211
+ result.scopesSupported = json.scopes_supported;
1212
+ if (json.bearer_methods_supported)
1213
+ result.bearerMethodsSupported = json.bearer_methods_supported;
1214
+ if (json.resource_signing_alg_values_supported)
1215
+ result.resourceSigningAlgValuesSupported = json.resource_signing_alg_values_supported;
1216
+ if (json.resource_name)
1217
+ result.resourceName = json.resource_name;
1218
+ if (json.resource_documentation)
1219
+ result.resourceDocumentation = json.resource_documentation;
1220
+ if (json.resource_policy_uri)
1221
+ result.resourcePolicyUri = json.resource_policy_uri;
1222
+ if (json.resource_tos_uri)
1223
+ result.resourceTosUri = json.resource_tos_uri;
1224
+ if (json.client_id)
1225
+ result.clientId = json.client_id;
1226
+ if (json.client_secret)
1227
+ result.clientSecret = json.client_secret;
1228
+ if (json.use_id_token_as_bearer)
1229
+ result.useIdTokenAsBearer = json.use_id_token_as_bearer;
1230
+ if (json.device_code_client_id)
1231
+ result.deviceCodeClientId = json.device_code_client_id;
1232
+ if (json.device_code_client_secret)
1233
+ result.deviceCodeClientSecret = json.device_code_client_secret;
1234
+ return result;
1235
+ }
1236
+ async function httpOAuthMetadata(baseUrl, prefix) {
1237
+ const effectivePrefix = (prefix ?? "").replace(/\/+$/, "");
1238
+ const metadataUrl = `${baseUrl.replace(/\/+$/, "")}/.well-known/oauth-protected-resource${effectivePrefix}`;
1239
+ try {
1240
+ return await fetchOAuthMetadata(metadataUrl);
1241
+ } catch {
1242
+ return null;
1243
+ }
1244
+ }
1245
+ async function fetchOAuthMetadata(metadataUrl) {
1246
+ const response = await fetch(metadataUrl);
1247
+ if (!response.ok) {
1248
+ throw new Error(`Failed to fetch OAuth metadata from ${metadataUrl}: ${response.status}`);
1249
+ }
1250
+ const json = await response.json();
1251
+ return parseMetadataJson(json);
1252
+ }
1253
+ function parseResourceMetadataUrl(wwwAuthenticate) {
1254
+ const bearerMatch = wwwAuthenticate.match(/^Bearer\s+(.*)/i);
1255
+ if (!bearerMatch)
1256
+ return null;
1257
+ const params = bearerMatch[1];
1258
+ const metadataMatch = params.match(/resource_metadata="([^"]+)"/);
1259
+ if (!metadataMatch)
1260
+ return null;
1261
+ return metadataMatch[1];
1262
+ }
1263
+ function parseClientId(wwwAuthenticate) {
1264
+ const bearerMatch = wwwAuthenticate.match(/^Bearer\s+(.*)/i);
1265
+ if (!bearerMatch)
1266
+ return null;
1267
+ const params = bearerMatch[1];
1268
+ const clientIdMatch = params.match(/client_id="([^"]+)"/);
1269
+ if (!clientIdMatch)
1270
+ return null;
1271
+ return clientIdMatch[1];
1272
+ }
1273
+ function parseClientSecret(wwwAuthenticate) {
1274
+ const bearerMatch = wwwAuthenticate.match(/^Bearer\s+(.*)/i);
1275
+ if (!bearerMatch)
1276
+ return null;
1277
+ const params = bearerMatch[1];
1278
+ const match = params.match(/client_secret="([^"]+)"/);
1279
+ if (!match)
1280
+ return null;
1281
+ return match[1];
1282
+ }
1283
+ function parseUseIdTokenAsBearer(wwwAuthenticate) {
1284
+ const bearerMatch = wwwAuthenticate.match(/^Bearer\s+(.*)/i);
1285
+ if (!bearerMatch)
1286
+ return false;
1287
+ const params = bearerMatch[1];
1288
+ const match = params.match(/use_id_token_as_bearer="([^"]+)"/);
1289
+ if (!match)
1290
+ return false;
1291
+ return match[1] === "true";
1292
+ }
1293
+ function parseDeviceCodeClientId(wwwAuthenticate) {
1294
+ const bearerMatch = wwwAuthenticate.match(/^Bearer\s+(.*)/i);
1295
+ if (!bearerMatch)
1296
+ return null;
1297
+ const params = bearerMatch[1];
1298
+ const match = params.match(/device_code_client_id="([^"]+)"/);
1299
+ if (!match)
1300
+ return null;
1301
+ return match[1];
1302
+ }
1303
+ function parseDeviceCodeClientSecret(wwwAuthenticate) {
1304
+ const bearerMatch = wwwAuthenticate.match(/^Bearer\s+(.*)/i);
1305
+ if (!bearerMatch)
1306
+ return null;
1307
+ const params = bearerMatch[1];
1308
+ const match = params.match(/device_code_client_secret="([^"]+)"/);
1309
+ if (!match)
1310
+ return null;
1311
+ return match[1];
1312
+ }
911
1313
  // src/client/pipe.ts
912
1314
  import {
913
1315
  Field as Field2,
914
- makeData as makeData4,
915
- RecordBatch as RecordBatch4,
916
- RecordBatchStreamWriter as RecordBatchStreamWriter2,
1316
+ makeData as makeData5,
1317
+ RecordBatch as RecordBatch5,
1318
+ RecordBatchStreamWriter as RecordBatchStreamWriter3,
917
1319
  Schema as Schema2,
918
- Struct as Struct4,
919
- vectorFromArray as vectorFromArray4
1320
+ Struct as Struct5,
1321
+ vectorFromArray as vectorFromArray5
920
1322
  } from "@query-farm/apache-arrow";
921
1323
  class PipeIncrementalWriter {
922
1324
  writer;
@@ -924,7 +1326,7 @@ class PipeIncrementalWriter {
924
1326
  closed = false;
925
1327
  constructor(writeFn, schema) {
926
1328
  this.writeFn = writeFn;
927
- this.writer = new RecordBatchStreamWriter2;
1329
+ this.writer = new RecordBatchStreamWriter3;
928
1330
  this.writer.reset(undefined, schema);
929
1331
  this.drain();
930
1332
  }
@@ -962,6 +1364,7 @@ class PipeStreamSession {
962
1364
  _outputSchema;
963
1365
  _releaseBusy;
964
1366
  _setDrainPromise;
1367
+ _externalConfig;
965
1368
  constructor(opts) {
966
1369
  this._reader = opts.reader;
967
1370
  this._writeFn = opts.writeFn;
@@ -970,6 +1373,7 @@ class PipeStreamSession {
970
1373
  this._outputSchema = opts.outputSchema;
971
1374
  this._releaseBusy = opts.releaseBusy;
972
1375
  this._setDrainPromise = opts.setDrainPromise;
1376
+ this._externalConfig = opts.externalConfig;
973
1377
  }
974
1378
  get header() {
975
1379
  return this._header;
@@ -980,6 +1384,9 @@ class PipeStreamSession {
980
1384
  if (batch === null)
981
1385
  return null;
982
1386
  if (batch.numRows === 0) {
1387
+ if (isExternalLocationBatch(batch)) {
1388
+ return await resolveExternalLocation(batch, this._externalConfig);
1389
+ }
983
1390
  if (dispatchLogOrError(batch, this._onLog)) {
984
1391
  continue;
985
1392
  }
@@ -1005,16 +1412,16 @@ class PipeStreamSession {
1005
1412
  if (input.length === 0) {
1006
1413
  inputSchema = this._inputSchema ?? this._outputSchema;
1007
1414
  const children = inputSchema.fields.map((f) => {
1008
- return makeData4({ type: f.type, length: 0, nullCount: 0 });
1415
+ return makeData5({ type: f.type, length: 0, nullCount: 0 });
1009
1416
  });
1010
- const structType = new Struct4(inputSchema.fields);
1011
- const data = makeData4({
1417
+ const structType = new Struct5(inputSchema.fields);
1418
+ const data = makeData5({
1012
1419
  type: structType,
1013
1420
  length: 0,
1014
1421
  children,
1015
1422
  nullCount: 0
1016
1423
  });
1017
- batch = new RecordBatch4(inputSchema, data);
1424
+ batch = new RecordBatch5(inputSchema, data);
1018
1425
  } else {
1019
1426
  const keys = Object.keys(input[0]);
1020
1427
  const fields = keys.map((key) => {
@@ -1039,16 +1446,16 @@ class PipeStreamSession {
1039
1446
  }
1040
1447
  const children = inputSchema.fields.map((f) => {
1041
1448
  const values = input.map((row) => row[f.name]);
1042
- return vectorFromArray4(values, f.type).data[0];
1449
+ return vectorFromArray5(values, f.type).data[0];
1043
1450
  });
1044
- const structType = new Struct4(inputSchema.fields);
1045
- const data = makeData4({
1451
+ const structType = new Struct5(inputSchema.fields);
1452
+ const data = makeData5({
1046
1453
  type: structType,
1047
1454
  length: input.length,
1048
1455
  children,
1049
1456
  nullCount: 0
1050
1457
  });
1051
- batch = new RecordBatch4(inputSchema, data);
1458
+ batch = new RecordBatch5(inputSchema, data);
1052
1459
  }
1053
1460
  if (!this._inputWriter) {
1054
1461
  this._inputWriter = new PipeIncrementalWriter(this._writeFn, inputSchema);
@@ -1087,14 +1494,14 @@ class PipeStreamSession {
1087
1494
  try {
1088
1495
  const tickSchema = new Schema2([]);
1089
1496
  this._inputWriter = new PipeIncrementalWriter(this._writeFn, tickSchema);
1090
- const structType = new Struct4(tickSchema.fields);
1091
- const tickData = makeData4({
1497
+ const structType = new Struct5(tickSchema.fields);
1498
+ const tickData = makeData5({
1092
1499
  type: structType,
1093
1500
  length: 0,
1094
1501
  children: [],
1095
1502
  nullCount: 0
1096
1503
  });
1097
- const tickBatch = new RecordBatch4(tickSchema, tickData);
1504
+ const tickBatch = new RecordBatch5(tickSchema, tickData);
1098
1505
  while (true) {
1099
1506
  this._inputWriter.write(tickBatch);
1100
1507
  await this._ensureOutputStream();
@@ -1149,6 +1556,7 @@ class PipeStreamSession {
1149
1556
  }
1150
1557
  function pipeConnect(readable, writable, options) {
1151
1558
  const onLog = options?.onLog;
1559
+ const externalConfig = options?.externalLocation;
1152
1560
  let reader = null;
1153
1561
  let readerPromise = null;
1154
1562
  let methodCache = null;
@@ -1224,10 +1632,14 @@ function pipeConnect(readable, writable, options) {
1224
1632
  throw new Error("EOF reading response");
1225
1633
  }
1226
1634
  let resultBatch = null;
1227
- for (const batch of response.batches) {
1635
+ for (let batch of response.batches) {
1228
1636
  if (batch.numRows === 0) {
1229
- dispatchLogOrError(batch, onLog);
1230
- continue;
1637
+ if (isExternalLocationBatch(batch)) {
1638
+ batch = await resolveExternalLocation(batch, externalConfig);
1639
+ } else {
1640
+ dispatchLogOrError(batch, onLog);
1641
+ continue;
1642
+ }
1231
1643
  }
1232
1644
  resultBatch = batch;
1233
1645
  }
@@ -1280,7 +1692,8 @@ function pipeConnect(readable, writable, options) {
1280
1692
  header,
1281
1693
  outputSchema,
1282
1694
  releaseBusy,
1283
- setDrainPromise
1695
+ setDrainPromise,
1696
+ externalConfig
1284
1697
  });
1285
1698
  } catch (e) {
1286
1699
  try {
@@ -1330,7 +1743,8 @@ function subprocessConnect(cmd, options) {
1330
1743
  }
1331
1744
  };
1332
1745
  const client = pipeConnect(stdout, writable, {
1333
- onLog: options?.onLog
1746
+ onLog: options?.onLog,
1747
+ externalLocation: options?.externalLocation
1334
1748
  });
1335
1749
  const originalClose = client.close;
1336
1750
  client.close = () => {
@@ -1341,127 +1755,144 @@ function subprocessConnect(cmd, options) {
1341
1755
  };
1342
1756
  return client;
1343
1757
  }
1344
- // src/http/handler.ts
1345
- import { randomBytes } from "node:crypto";
1346
- import { Schema as Schema5 } from "@query-farm/apache-arrow";
1347
-
1348
- // src/types.ts
1349
- import { RecordBatch as RecordBatch6, recordBatchFromArrays } from "@query-farm/apache-arrow";
1350
-
1351
- // src/wire/response.ts
1352
- import {
1353
- Data,
1354
- DataType as DataType2,
1355
- makeData as makeData5,
1356
- RecordBatch as RecordBatch5,
1357
- Struct as Struct5,
1358
- vectorFromArray as vectorFromArray5
1359
- } from "@query-farm/apache-arrow";
1360
- function coerceInt64(schema, values) {
1361
- const result = { ...values };
1362
- for (const field of schema.fields) {
1363
- const val = result[field.name];
1364
- if (val === undefined)
1365
- continue;
1366
- if (!DataType2.isInt(field.type) || field.type.bitWidth !== 64)
1367
- continue;
1368
- if (Array.isArray(val)) {
1369
- result[field.name] = val.map((v) => typeof v === "number" ? BigInt(v) : v);
1370
- } else if (typeof val === "number") {
1371
- result[field.name] = BigInt(val);
1758
+ // src/http/auth.ts
1759
+ function oauthResourceMetadataToJson(metadata) {
1760
+ const json = {
1761
+ resource: metadata.resource,
1762
+ authorization_servers: metadata.authorizationServers
1763
+ };
1764
+ if (metadata.scopesSupported)
1765
+ json.scopes_supported = metadata.scopesSupported;
1766
+ if (metadata.bearerMethodsSupported)
1767
+ json.bearer_methods_supported = metadata.bearerMethodsSupported;
1768
+ if (metadata.resourceSigningAlgValuesSupported)
1769
+ json.resource_signing_alg_values_supported = metadata.resourceSigningAlgValuesSupported;
1770
+ if (metadata.resourceName)
1771
+ json.resource_name = metadata.resourceName;
1772
+ if (metadata.resourceDocumentation)
1773
+ json.resource_documentation = metadata.resourceDocumentation;
1774
+ if (metadata.resourcePolicyUri)
1775
+ json.resource_policy_uri = metadata.resourcePolicyUri;
1776
+ if (metadata.resourceTosUri)
1777
+ json.resource_tos_uri = metadata.resourceTosUri;
1778
+ if (metadata.clientId) {
1779
+ if (!/^[A-Za-z0-9\-._~]+$/.test(metadata.clientId)) {
1780
+ throw new Error(`Invalid client_id: must contain only URL-safe characters [A-Za-z0-9\\-._~]`);
1372
1781
  }
1782
+ json.client_id = metadata.clientId;
1373
1783
  }
1374
- return result;
1375
- }
1376
- function buildResultBatch(schema, values, serverId, requestId) {
1377
- const metadata = new Map;
1378
- metadata.set(SERVER_ID_KEY, serverId);
1379
- if (requestId !== null) {
1380
- metadata.set(REQUEST_ID_KEY, requestId);
1381
- }
1382
- if (schema.fields.length === 0) {
1383
- return buildEmptyBatch(schema, metadata);
1784
+ if (metadata.clientSecret) {
1785
+ if (!/^[A-Za-z0-9\-._~]+$/.test(metadata.clientSecret)) {
1786
+ throw new Error(`Invalid client_secret: must contain only URL-safe characters [A-Za-z0-9\\-._~]`);
1787
+ }
1788
+ json.client_secret = metadata.clientSecret;
1384
1789
  }
1385
- for (const field of schema.fields) {
1386
- if (values[field.name] === undefined && !field.nullable) {
1387
- const got = Object.keys(values);
1388
- throw new TypeError(`Handler result missing required field '${field.name}'. Got keys: [${got.join(", ")}]`);
1790
+ if (metadata.deviceCodeClientId) {
1791
+ if (!/^[A-Za-z0-9\-._~]+$/.test(metadata.deviceCodeClientId)) {
1792
+ throw new Error(`Invalid device_code_client_id: must contain only URL-safe characters [A-Za-z0-9\\-._~]`);
1389
1793
  }
1794
+ json.device_code_client_id = metadata.deviceCodeClientId;
1390
1795
  }
1391
- const coerced = coerceInt64(schema, values);
1392
- const children = schema.fields.map((f) => {
1393
- const val = coerced[f.name];
1394
- if (val instanceof Data) {
1395
- return val;
1796
+ if (metadata.deviceCodeClientSecret) {
1797
+ if (!/^[A-Za-z0-9\-._~]+$/.test(metadata.deviceCodeClientSecret)) {
1798
+ throw new Error(`Invalid device_code_client_secret: must contain only URL-safe characters [A-Za-z0-9\\-._~]`);
1396
1799
  }
1397
- const arr = vectorFromArray5([val], f.type);
1398
- return arr.data[0];
1399
- });
1400
- const structType = new Struct5(schema.fields);
1401
- const data = makeData5({
1402
- type: structType,
1403
- length: 1,
1404
- children,
1405
- nullCount: 0
1406
- });
1407
- return new RecordBatch5(schema, data, metadata);
1408
- }
1409
- function buildErrorBatch(schema, error, serverId, requestId) {
1410
- const metadata = new Map;
1411
- metadata.set(LOG_LEVEL_KEY, "EXCEPTION");
1412
- metadata.set(LOG_MESSAGE_KEY, `${error.constructor.name}: ${error.message}`);
1413
- const extra = {
1414
- exception_type: error.constructor.name,
1415
- exception_message: error.message,
1416
- traceback: error.stack ?? ""
1417
- };
1418
- metadata.set(LOG_EXTRA_KEY, JSON.stringify(extra));
1419
- metadata.set(SERVER_ID_KEY, serverId);
1420
- if (requestId !== null) {
1421
- metadata.set(REQUEST_ID_KEY, requestId);
1800
+ json.device_code_client_secret = metadata.deviceCodeClientSecret;
1422
1801
  }
1423
- return buildEmptyBatch(schema, metadata);
1802
+ if (metadata.useIdTokenAsBearer) {
1803
+ json.use_id_token_as_bearer = true;
1804
+ }
1805
+ return json;
1424
1806
  }
1425
- function buildLogBatch(schema, level, message, extra, serverId, requestId) {
1426
- const metadata = new Map;
1427
- metadata.set(LOG_LEVEL_KEY, level);
1428
- metadata.set(LOG_MESSAGE_KEY, message);
1429
- if (extra) {
1430
- metadata.set(LOG_EXTRA_KEY, JSON.stringify(extra));
1807
+ function wellKnownPath(prefix) {
1808
+ return `/.well-known/oauth-protected-resource${prefix}`;
1809
+ }
1810
+ function buildWwwAuthenticateHeader(metadataUrl, clientId, clientSecret, useIdTokenAsBearer, deviceCodeClientId, deviceCodeClientSecret) {
1811
+ let header = "Bearer";
1812
+ if (metadataUrl) {
1813
+ header += ` resource_metadata="${metadataUrl}"`;
1431
1814
  }
1432
- if (serverId != null) {
1433
- metadata.set(SERVER_ID_KEY, serverId);
1815
+ if (clientId) {
1816
+ header += `, client_id="${clientId}"`;
1434
1817
  }
1435
- if (requestId != null) {
1436
- metadata.set(REQUEST_ID_KEY, requestId);
1818
+ if (clientSecret) {
1819
+ header += `, client_secret="${clientSecret}"`;
1437
1820
  }
1438
- return buildEmptyBatch(schema, metadata);
1821
+ if (deviceCodeClientId) {
1822
+ header += `, device_code_client_id="${deviceCodeClientId}"`;
1823
+ }
1824
+ if (deviceCodeClientSecret) {
1825
+ header += `, device_code_client_secret="${deviceCodeClientSecret}"`;
1826
+ }
1827
+ if (useIdTokenAsBearer) {
1828
+ header += `, use_id_token_as_bearer="true"`;
1829
+ }
1830
+ return header;
1439
1831
  }
1440
- function buildEmptyBatch(schema, metadata) {
1441
- const children = schema.fields.map((f) => {
1442
- return makeData5({ type: f.type, length: 0, nullCount: 0 });
1443
- });
1444
- if (schema.fields.length === 0) {
1445
- const structType2 = new Struct5(schema.fields);
1446
- const data2 = makeData5({
1447
- type: structType2,
1448
- length: 0,
1449
- children: [],
1450
- nullCount: 0
1451
- });
1452
- return new RecordBatch5(schema, data2, metadata);
1832
+ // src/http/bearer.ts
1833
+ import { timingSafeEqual } from "node:crypto";
1834
+ function bearerAuthenticate(options) {
1835
+ const { validate } = options;
1836
+ return async function authenticate(request) {
1837
+ const authHeader = request.headers.get("Authorization") ?? "";
1838
+ if (!authHeader.startsWith("Bearer ")) {
1839
+ throw new Error("Missing or invalid Authorization header");
1840
+ }
1841
+ const token = authHeader.slice(7);
1842
+ return validate(token);
1843
+ };
1844
+ }
1845
+ function safeEqual(a, b) {
1846
+ const enc = new TextEncoder;
1847
+ const bufA = enc.encode(a);
1848
+ const bufB = enc.encode(b);
1849
+ if (bufA.byteLength !== bufB.byteLength)
1850
+ return false;
1851
+ return timingSafeEqual(bufA, bufB);
1852
+ }
1853
+ function bearerAuthenticateStatic(options) {
1854
+ const entries = options.tokens instanceof Map ? [...options.tokens.entries()] : Object.entries(options.tokens);
1855
+ function validate(token) {
1856
+ for (const [key, ctx] of entries) {
1857
+ if (safeEqual(token, key))
1858
+ return ctx;
1859
+ }
1860
+ throw new Error("Unknown bearer token");
1453
1861
  }
1454
- const structType = new Struct5(schema.fields);
1455
- const data = makeData5({
1456
- type: structType,
1457
- length: 0,
1458
- children,
1459
- nullCount: 0
1460
- });
1461
- return new RecordBatch5(schema, data, metadata);
1862
+ return bearerAuthenticate({ validate });
1863
+ }
1864
+ function isCredentialError(err) {
1865
+ return err instanceof Error && err.constructor === Error && err.name !== "PermissionError";
1866
+ }
1867
+ function chainAuthenticate(...authenticators) {
1868
+ if (authenticators.length === 0) {
1869
+ throw new Error("chainAuthenticate requires at least one authenticator");
1870
+ }
1871
+ return async function authenticate(request) {
1872
+ let lastError = null;
1873
+ for (const authFn of authenticators) {
1874
+ try {
1875
+ return await authFn(request);
1876
+ } catch (err) {
1877
+ if (isCredentialError(err)) {
1878
+ lastError = err;
1879
+ continue;
1880
+ }
1881
+ throw err;
1882
+ }
1883
+ }
1884
+ const error = new Error("No authenticator accepted the request");
1885
+ if (lastError)
1886
+ error.cause = lastError;
1887
+ throw error;
1888
+ };
1462
1889
  }
1890
+ // src/http/handler.ts
1891
+ import { randomBytes } from "node:crypto";
1892
+ import { Schema as Schema5 } from "@query-farm/apache-arrow";
1463
1893
 
1464
1894
  // src/types.ts
1895
+ import { RecordBatch as RecordBatch6, recordBatchFromArrays } from "@query-farm/apache-arrow";
1465
1896
  var MethodType;
1466
1897
  ((MethodType2) => {
1467
1898
  MethodType2["UNARY"] = "unary";
@@ -1476,11 +1907,13 @@ class OutputCollector {
1476
1907
  _outputSchema;
1477
1908
  _serverId;
1478
1909
  _requestId;
1479
- constructor(outputSchema, producerMode = true, serverId = "", requestId = null) {
1910
+ auth;
1911
+ constructor(outputSchema, producerMode = true, serverId = "", requestId = null, authContext) {
1480
1912
  this._outputSchema = outputSchema;
1481
1913
  this._producerMode = producerMode;
1482
1914
  this._serverId = serverId;
1483
1915
  this._requestId = requestId;
1916
+ this.auth = authContext ?? AuthContext.anonymous();
1484
1917
  }
1485
1918
  get outputSchema() {
1486
1919
  return this._outputSchema;
@@ -1528,7 +1961,7 @@ class OutputCollector {
1528
1961
  init_zstd();
1529
1962
 
1530
1963
  // src/http/dispatch.ts
1531
- import { RecordBatch as RecordBatch8, RecordBatchReader as RecordBatchReader5, Schema as Schema4 } from "@query-farm/apache-arrow";
1964
+ import { RecordBatch as RecordBatch8, RecordBatchReader as RecordBatchReader6, Schema as Schema4 } from "@query-farm/apache-arrow";
1532
1965
 
1533
1966
  // src/dispatch/describe.ts
1534
1967
  import {
@@ -1544,9 +1977,9 @@ import {
1544
1977
  } from "@query-farm/apache-arrow";
1545
1978
 
1546
1979
  // src/util/schema.ts
1547
- import { RecordBatchStreamWriter as RecordBatchStreamWriter3 } from "@query-farm/apache-arrow";
1980
+ import { RecordBatchStreamWriter as RecordBatchStreamWriter4 } from "@query-farm/apache-arrow";
1548
1981
  function serializeSchema(schema) {
1549
- const writer = new RecordBatchStreamWriter3;
1982
+ const writer = new RecordBatchStreamWriter4;
1550
1983
  writer.reset(undefined, schema);
1551
1984
  writer.close();
1552
1985
  return writer.toUint8Array(true);
@@ -1563,7 +1996,9 @@ var DESCRIBE_SCHEMA = new Schema3([
1563
1996
  new Field3("param_types_json", new Utf82, true),
1564
1997
  new Field3("param_defaults_json", new Utf82, true),
1565
1998
  new Field3("has_header", new Bool2, false),
1566
- new Field3("header_schema_ipc", new Binary2, true)
1999
+ new Field3("header_schema_ipc", new Binary2, true),
2000
+ new Field3("is_exchange", new Bool2, true),
2001
+ new Field3("param_docs_json", new Utf82, true)
1567
2002
  ]);
1568
2003
  function buildDescribeBatch(protocolName, methods, serverId) {
1569
2004
  const sortedEntries = [...methods.entries()].sort(([a], [b]) => a.localeCompare(b));
@@ -1577,6 +2012,8 @@ function buildDescribeBatch(protocolName, methods, serverId) {
1577
2012
  const paramDefaultsJsons = [];
1578
2013
  const hasHeaders = [];
1579
2014
  const headerSchemas = [];
2015
+ const isExchanges = [];
2016
+ const paramDocsJsons = [];
1580
2017
  for (const [name, method] of sortedEntries) {
1581
2018
  names.push(name);
1582
2019
  methodTypes.push(method.type);
@@ -1603,6 +2040,14 @@ function buildDescribeBatch(protocolName, methods, serverId) {
1603
2040
  }
1604
2041
  hasHeaders.push(!!method.headerSchema);
1605
2042
  headerSchemas.push(method.headerSchema ? serializeSchema(method.headerSchema) : null);
2043
+ if (method.exchangeFn) {
2044
+ isExchanges.push(true);
2045
+ } else if (method.producerFn) {
2046
+ isExchanges.push(false);
2047
+ } else {
2048
+ isExchanges.push(null);
2049
+ }
2050
+ paramDocsJsons.push(null);
1606
2051
  }
1607
2052
  const nameArr = vectorFromArray6(names, new Utf82);
1608
2053
  const methodTypeArr = vectorFromArray6(methodTypes, new Utf82);
@@ -1614,6 +2059,8 @@ function buildDescribeBatch(protocolName, methods, serverId) {
1614
2059
  const paramDefaultsArr = vectorFromArray6(paramDefaultsJsons, new Utf82);
1615
2060
  const hasHeaderArr = vectorFromArray6(hasHeaders, new Bool2);
1616
2061
  const headerSchemaArr = vectorFromArray6(headerSchemas, new Binary2);
2062
+ const isExchangeArr = vectorFromArray6(isExchanges, new Bool2);
2063
+ const paramDocsArr = vectorFromArray6(paramDocsJsons, new Utf82);
1617
2064
  const children = [
1618
2065
  nameArr.data[0],
1619
2066
  methodTypeArr.data[0],
@@ -1624,7 +2071,9 @@ function buildDescribeBatch(protocolName, methods, serverId) {
1624
2071
  paramTypesArr.data[0],
1625
2072
  paramDefaultsArr.data[0],
1626
2073
  hasHeaderArr.data[0],
1627
- headerSchemaArr.data[0]
2074
+ headerSchemaArr.data[0],
2075
+ isExchangeArr.data[0],
2076
+ paramDocsArr.data[0]
1628
2077
  ];
1629
2078
  const structType = new Struct6(DESCRIBE_SCHEMA.fields);
1630
2079
  const data = makeData6({
@@ -1687,7 +2136,7 @@ function parseRequest(schema, batch) {
1687
2136
  }
1688
2137
 
1689
2138
  // src/http/token.ts
1690
- import { createHmac, timingSafeEqual } from "node:crypto";
2139
+ import { createHmac, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
1691
2140
  var TOKEN_VERSION = 2;
1692
2141
  var HMAC_LEN = 32;
1693
2142
  var MIN_TOKEN_LEN = 1 + 8 + 12 + HMAC_LEN;
@@ -1724,7 +2173,7 @@ function unpackStateToken(tokenBase64, signingKey, tokenTtl) {
1724
2173
  const payload = token.subarray(0, token.length - HMAC_LEN);
1725
2174
  const receivedMac = token.subarray(token.length - HMAC_LEN);
1726
2175
  const expectedMac = createHmac("sha256", signingKey).update(payload).digest();
1727
- if (!timingSafeEqual(receivedMac, expectedMac)) {
2176
+ if (!timingSafeEqual2(receivedMac, expectedMac)) {
1728
2177
  throw new Error("State token HMAC verification failed");
1729
2178
  }
1730
2179
  let offset = 0;
@@ -1766,7 +2215,7 @@ function unpackStateToken(tokenBase64, signingKey, tokenTtl) {
1766
2215
 
1767
2216
  // src/http/dispatch.ts
1768
2217
  async function deserializeSchema2(bytes) {
1769
- const reader = await RecordBatchReader5.from(bytes);
2218
+ const reader = await RecordBatchReader6.from(bytes);
1770
2219
  await reader.open();
1771
2220
  return reader.schema;
1772
2221
  }
@@ -1783,15 +2232,20 @@ async function httpDispatchUnary(method, body, ctx) {
1783
2232
  if (parsed.methodName !== method.name) {
1784
2233
  throw new HttpRpcError(`Method name in request '${parsed.methodName}' does not match URL '${method.name}'`, 400);
1785
2234
  }
1786
- const out = new OutputCollector(schema, true, ctx.serverId, parsed.requestId);
2235
+ const out = new OutputCollector(schema, true, ctx.serverId, parsed.requestId, ctx.authContext);
1787
2236
  try {
1788
2237
  const result = await method.handler(parsed.params, out);
1789
- const resultBatch = buildResultBatch(schema, result, ctx.serverId, parsed.requestId);
2238
+ let resultBatch = buildResultBatch(schema, result, ctx.serverId, parsed.requestId);
2239
+ if (ctx.externalLocation) {
2240
+ resultBatch = await maybeExternalizeBatch(resultBatch, ctx.externalLocation);
2241
+ }
1790
2242
  const batches = [...out.batches.map((b) => b.batch), resultBatch];
1791
2243
  return arrowResponse(serializeIpcStream(schema, batches));
1792
2244
  } catch (error) {
1793
2245
  const errBatch = buildErrorBatch(schema, error, ctx.serverId, parsed.requestId);
1794
- return arrowResponse(serializeIpcStream(schema, [errBatch]), 500);
2246
+ const response = arrowResponse(serializeIpcStream(schema, [errBatch]), 500);
2247
+ response.__dispatchError = error;
2248
+ return response;
1795
2249
  }
1796
2250
  }
1797
2251
  async function httpDispatchStreamInit(method, body, ctx) {
@@ -1813,21 +2267,25 @@ async function httpDispatchStreamInit(method, body, ctx) {
1813
2267
  } catch (error) {
1814
2268
  const errSchema = method.headerSchema ?? EMPTY_SCHEMA;
1815
2269
  const errBatch = buildErrorBatch(errSchema, error, ctx.serverId, parsed.requestId);
1816
- return arrowResponse(serializeIpcStream(errSchema, [errBatch]), 500);
2270
+ const response = arrowResponse(serializeIpcStream(errSchema, [errBatch]), 500);
2271
+ response.__dispatchError = error;
2272
+ return response;
1817
2273
  }
1818
2274
  const resolvedOutputSchema = state?.__outputSchema ?? outputSchema;
1819
2275
  const effectiveProducer = state?.__isProducer ?? isProducer;
1820
2276
  let headerBytes = null;
1821
2277
  if (method.headerSchema && method.headerInit) {
1822
2278
  try {
1823
- const headerOut = new OutputCollector(method.headerSchema, true, ctx.serverId, parsed.requestId);
2279
+ const headerOut = new OutputCollector(method.headerSchema, true, ctx.serverId, parsed.requestId, ctx.authContext);
1824
2280
  const headerValues = method.headerInit(parsed.params, state, headerOut);
1825
2281
  const headerBatch = buildResultBatch(method.headerSchema, headerValues, ctx.serverId, parsed.requestId);
1826
2282
  const headerBatches = [...headerOut.batches.map((b) => b.batch), headerBatch];
1827
2283
  headerBytes = serializeIpcStream(method.headerSchema, headerBatches);
1828
2284
  } catch (error) {
1829
2285
  const errBatch = buildErrorBatch(method.headerSchema, error, ctx.serverId, parsed.requestId);
1830
- return arrowResponse(serializeIpcStream(method.headerSchema, [errBatch]), 500);
2286
+ const response = arrowResponse(serializeIpcStream(method.headerSchema, [errBatch]), 500);
2287
+ response.__dispatchError = error;
2288
+ return response;
1831
2289
  }
1832
2290
  }
1833
2291
  if (effectiveProducer) {
@@ -1888,7 +2346,7 @@ async function httpDispatchStreamExchange(method, body, ctx) {
1888
2346
  if (effectiveProducer) {
1889
2347
  return produceStreamResponse(method, state, outputSchema, inputSchema, ctx, null, null);
1890
2348
  } else {
1891
- const out = new OutputCollector(outputSchema, effectiveProducer, ctx.serverId, null);
2349
+ const out = new OutputCollector(outputSchema, effectiveProducer, ctx.serverId, null, ctx.authContext);
1892
2350
  const conformedBatch = conformBatchToSchema(reqBatch, inputSchema);
1893
2351
  try {
1894
2352
  if (method.exchangeFn) {
@@ -1902,7 +2360,9 @@ async function httpDispatchStreamExchange(method, body, ctx) {
1902
2360
  `).slice(0, 5).join(`
1903
2361
  `));
1904
2362
  const errBatch = buildErrorBatch(outputSchema, error, ctx.serverId, null);
1905
- return arrowResponse(serializeIpcStream(outputSchema, [errBatch]), 500);
2363
+ const response = arrowResponse(serializeIpcStream(outputSchema, [errBatch]), 500);
2364
+ response.__dispatchError = error;
2365
+ return response;
1906
2366
  }
1907
2367
  const batches = [];
1908
2368
  if (out.finished) {
@@ -1937,8 +2397,9 @@ async function produceStreamResponse(method, state, outputSchema, inputSchema, c
1937
2397
  const allBatches = [];
1938
2398
  const maxBytes = ctx.maxStreamResponseBytes;
1939
2399
  let estimatedBytes = 0;
2400
+ let producerError;
1940
2401
  while (true) {
1941
- const out = new OutputCollector(outputSchema, true, ctx.serverId, requestId);
2402
+ const out = new OutputCollector(outputSchema, true, ctx.serverId, requestId, ctx.authContext);
1942
2403
  try {
1943
2404
  if (method.producerFn) {
1944
2405
  await method.producerFn(state, out);
@@ -1952,6 +2413,7 @@ async function produceStreamResponse(method, state, outputSchema, inputSchema, c
1952
2413
  `).slice(0, 3).join(`
1953
2414
  `));
1954
2415
  allBatches.push(buildErrorBatch(outputSchema, error, ctx.serverId, requestId));
2416
+ producerError = error instanceof Error ? error : new Error(String(error));
1955
2417
  break;
1956
2418
  }
1957
2419
  for (const emitted of out.batches) {
@@ -1981,7 +2443,11 @@ async function produceStreamResponse(method, state, outputSchema, inputSchema, c
1981
2443
  } else {
1982
2444
  responseBody = dataBytes;
1983
2445
  }
1984
- return arrowResponse(responseBody);
2446
+ const response = arrowResponse(responseBody);
2447
+ if (producerError) {
2448
+ response.__dispatchError = producerError;
2449
+ }
2450
+ return response;
1985
2451
  }
1986
2452
  function concatBytes(...arrays) {
1987
2453
  const totalLen = arrays.reduce((sum, a) => sum + a.length, 0);
@@ -1994,6 +2460,261 @@ function concatBytes(...arrays) {
1994
2460
  return result;
1995
2461
  }
1996
2462
 
2463
+ // src/http/pages.ts
2464
+ var LOGO_URL = "https://vgi-rpc-python.query.farm/assets/logo-hero.png";
2465
+ var FONTS = `<link rel="preconnect" href="https://fonts.googleapis.com">
2466
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
2467
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">`;
2468
+ function escapeHtml(s) {
2469
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2470
+ }
2471
+ function arrowTypeToString(type) {
2472
+ const id = type.typeId;
2473
+ if (id === 5)
2474
+ return "str";
2475
+ if (id === 4)
2476
+ return "bytes";
2477
+ if (id === 2)
2478
+ return "int";
2479
+ if (id === 3)
2480
+ return "float";
2481
+ if (id === 6)
2482
+ return "bool";
2483
+ if (id === 12)
2484
+ return "list";
2485
+ if (id === 17)
2486
+ return "map";
2487
+ if (id === 24)
2488
+ return "enum";
2489
+ return type.toString();
2490
+ }
2491
+ function buildLandingPage(protocolName, serverId, describePath, repoUrl) {
2492
+ const links = [];
2493
+ if (describePath) {
2494
+ links.push(`<a class="primary" href="${escapeHtml(describePath)}">View service API</a>`);
2495
+ }
2496
+ if (repoUrl) {
2497
+ links.push(`<a href="${escapeHtml(repoUrl)}">Source repository</a>`);
2498
+ }
2499
+ links.push(`<a href="https://vgi-rpc.query.farm">Learn more about <code>vgi-rpc</code></a>`);
2500
+ return `<!DOCTYPE html>
2501
+ <html lang="en">
2502
+ <head>
2503
+ <meta charset="utf-8">
2504
+ <meta name="viewport" content="width=device-width, initial-scale=1">
2505
+ <title>${escapeHtml(protocolName)} — vgi-rpc</title>
2506
+ ${FONTS}
2507
+ <style>
2508
+ body { font-family: 'Inter', system-ui, -apple-system, sans-serif; max-width: 600px;
2509
+ margin: 0 auto; padding: 60px 20px 0; color: #2c2c1e; text-align: center;
2510
+ background: #faf8f0; }
2511
+ .logo { margin-bottom: 24px; }
2512
+ .logo img { width: 140px; height: 140px; border-radius: 50%;
2513
+ box-shadow: 0 4px 24px rgba(0,0,0,0.12); }
2514
+ h1 { color: #2d5016; margin-bottom: 8px; font-weight: 700; }
2515
+ code { font-family: 'JetBrains Mono', monospace; background: #f0ece0;
2516
+ padding: 2px 6px; border-radius: 3px; font-size: 0.9em; color: #2c2c1e; }
2517
+ a { color: #2d5016; text-decoration: none; }
2518
+ a:hover { color: #4a7c23; }
2519
+ p { line-height: 1.7; color: #6b6b5a; }
2520
+ .meta { font-size: 0.9em; color: #6b6b5a; }
2521
+ .links { margin-top: 28px; display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
2522
+ .links a { display: inline-block; padding: 8px 18px; border-radius: 6px;
2523
+ border: 1px solid #4a7c23; color: #2d5016; font-weight: 600;
2524
+ font-size: 0.9em; transition: all 0.2s ease; }
2525
+ .links a:hover { background: #4a7c23; color: #fff; }
2526
+ .links a.primary { background: #2d5016; color: #fff; border-color: #2d5016; }
2527
+ .links a.primary:hover { background: #4a7c23; border-color: #4a7c23; }
2528
+ footer { margin-top: 48px; padding: 20px 0; border-top: 1px solid #f0ece0;
2529
+ color: #6b6b5a; font-size: 0.85em; }
2530
+ footer a { color: #2d5016; font-weight: 600; }
2531
+ footer a:hover { color: #4a7c23; }
2532
+ </style>
2533
+ </head>
2534
+ <body>
2535
+ <div class="logo">
2536
+ <img src="${LOGO_URL}" alt="vgi-rpc logo">
2537
+ </div>
2538
+ <h1>${escapeHtml(protocolName)}</h1>
2539
+ <p class="meta">Powered by <code>vgi-rpc</code> (TypeScript) &middot; server <code>${escapeHtml(serverId)}</code></p>
2540
+ <p>This is a <code>vgi-rpc</code> service endpoint.</p>
2541
+ <div class="links">
2542
+ ${links.join(`
2543
+ `)}
2544
+ </div>
2545
+ <footer>
2546
+ &copy; 2026 &#x1F69C; <a href="https://query.farm">Query.Farm LLC</a>
2547
+ </footer>
2548
+ </body>
2549
+ </html>`;
2550
+ }
2551
+ function buildNotFoundPage(prefix, protocolName) {
2552
+ const nameFragment = protocolName ? ` (<strong>${escapeHtml(protocolName)}</strong>)` : "";
2553
+ const prefixDisplay = prefix || "/";
2554
+ return `<!DOCTYPE html>
2555
+ <html lang="en">
2556
+ <head>
2557
+ <meta charset="utf-8">
2558
+ <meta name="viewport" content="width=device-width, initial-scale=1">
2559
+ <title>404 — vgi-rpc endpoint</title>
2560
+ <style>
2561
+ body { font-family: system-ui, -apple-system, sans-serif; max-width: 600px;
2562
+ margin: 60px auto; padding: 0 20px; color: #333; text-align: center; }
2563
+ .logo { margin-bottom: 24px; }
2564
+ .logo img { width: 120px; height: 120px; }
2565
+ h1 { color: #555; }
2566
+ code { background: #f4f4f4; padding: 2px 6px; border-radius: 3px; font-size: 0.95em; }
2567
+ a { color: #0066cc; }
2568
+ p { line-height: 1.6; }
2569
+ </style>
2570
+ </head>
2571
+ <body>
2572
+ <div class="logo">
2573
+ <img src="${LOGO_URL}" alt="vgi-rpc logo">
2574
+ </div>
2575
+ <h1>404 — Not Found</h1>
2576
+ <p>This is a <code>vgi-rpc</code> service endpoint${nameFragment}.</p>
2577
+ <p>RPC methods are available under <code>${escapeHtml(prefixDisplay)}/&lt;method&gt;</code>.</p>
2578
+ <p>Learn more at <a href="https://vgi-rpc.query.farm">vgi-rpc.query.farm</a>.</p>
2579
+ </body>
2580
+ </html>`;
2581
+ }
2582
+ function buildMethodCard(method) {
2583
+ const name = escapeHtml(method.name);
2584
+ const isUnary = method.type === "unary";
2585
+ const hasHeader = !!method.headerSchema;
2586
+ const badges = [];
2587
+ badges.push(isUnary ? `<span class="badge badge-unary">unary</span>` : `<span class="badge badge-stream">stream</span>`);
2588
+ if (hasHeader)
2589
+ badges.push(`<span class="badge badge-header">header</span>`);
2590
+ let paramsHtml = "";
2591
+ const paramsSchema = method.paramsSchema;
2592
+ if (paramsSchema.fields.length > 0) {
2593
+ const rows = paramsSchema.fields.map((f) => {
2594
+ const paramName = escapeHtml(f.name);
2595
+ const paramType = escapeHtml(arrowTypeToString(f.type));
2596
+ const defaultVal = method.defaults && f.name in method.defaults ? escapeHtml(JSON.stringify(method.defaults[f.name])) : "&mdash;";
2597
+ return `<tr><td><code>${paramName}</code></td><td><code>${paramType}</code></td><td>${defaultVal}</td><td>&mdash;</td></tr>`;
2598
+ });
2599
+ paramsHtml = `<div class="section-label">Parameters</div>
2600
+ <table><tr><th>Name</th><th>Type</th><th>Default</th><th>Description</th></tr>
2601
+ ${rows.join(`
2602
+ `)}
2603
+ </table>`;
2604
+ } else {
2605
+ paramsHtml = `<p class="no-params">No parameters</p>`;
2606
+ }
2607
+ let returnsHtml = "";
2608
+ if (isUnary && method.resultSchema.fields.length > 0) {
2609
+ const rows = method.resultSchema.fields.map((f) => {
2610
+ return `<tr><td><code>${escapeHtml(f.name)}</code></td><td><code>${escapeHtml(arrowTypeToString(f.type))}</code></td></tr>`;
2611
+ });
2612
+ returnsHtml = `<div class="section-label">Returns</div>
2613
+ <table><tr><th>Name</th><th>Type</th></tr>
2614
+ ${rows.join(`
2615
+ `)}
2616
+ </table>`;
2617
+ }
2618
+ let headerHtml = "";
2619
+ if (hasHeader && method.headerSchema && method.headerSchema.fields.length > 0) {
2620
+ const rows = method.headerSchema.fields.map((f) => {
2621
+ return `<tr><td><code>${escapeHtml(f.name)}</code></td><td><code>${escapeHtml(arrowTypeToString(f.type))}</code></td></tr>`;
2622
+ });
2623
+ headerHtml = `<div class="section-label">Stream Header</div>
2624
+ <table><tr><th>Name</th><th>Type</th></tr>
2625
+ ${rows.join(`
2626
+ `)}
2627
+ </table>`;
2628
+ }
2629
+ const docHtml = method.doc ? `<p class="docstring">${escapeHtml(method.doc)}</p>` : "";
2630
+ return `<div class="card">
2631
+ <div class="card-header">
2632
+ <span class="method-name">${name}</span>
2633
+ ${badges.join(`
2634
+ `)}
2635
+ </div>
2636
+ ${docHtml}
2637
+ ${paramsHtml}
2638
+ ${returnsHtml}
2639
+ ${headerHtml}
2640
+ </div>`;
2641
+ }
2642
+ function buildDescribePage(protocolName, serverId, methods, repoUrl) {
2643
+ const sortedMethods = [...methods.entries()].filter(([name]) => name !== "__describe__").sort(([a], [b]) => a.localeCompare(b));
2644
+ const cards = sortedMethods.map(([, method]) => buildMethodCard(method)).join(`
2645
+ `);
2646
+ const repoLink = repoUrl ? ` &middot; <a href="${escapeHtml(repoUrl)}">Source</a>` : "";
2647
+ return `<!DOCTYPE html>
2648
+ <html lang="en">
2649
+ <head>
2650
+ <meta charset="utf-8">
2651
+ <meta name="viewport" content="width=device-width, initial-scale=1">
2652
+ <title>${escapeHtml(protocolName)} API Reference — vgi-rpc</title>
2653
+ ${FONTS}
2654
+ <style>
2655
+ body { font-family: 'Inter', system-ui, -apple-system, sans-serif; max-width: 900px;
2656
+ margin: 0 auto; padding: 40px 20px 0; color: #2c2c1e; background: #faf8f0; }
2657
+ .header { text-align: center; margin-bottom: 40px; }
2658
+ .header .logo img { width: 80px; height: 80px; border-radius: 50%;
2659
+ box-shadow: 0 3px 16px rgba(0,0,0,0.10); }
2660
+ .header h1 { margin-bottom: 4px; color: #2d5016; font-weight: 700; }
2661
+ .header .subtitle { color: #6b6b5a; font-size: 1.1em; margin-top: 0; }
2662
+ .header .meta { color: #6b6b5a; font-size: 0.9em; }
2663
+ .header .meta a { color: #2d5016; font-weight: 600; }
2664
+ .header .meta a:hover { color: #4a7c23; }
2665
+ code { font-family: 'JetBrains Mono', monospace; background: #f0ece0;
2666
+ padding: 2px 6px; border-radius: 3px; font-size: 0.85em; color: #2c2c1e; }
2667
+ a { color: #2d5016; text-decoration: none; }
2668
+ a:hover { color: #4a7c23; }
2669
+ .card { border: 1px solid #f0ece0; border-radius: 8px; padding: 20px;
2670
+ margin-bottom: 16px; background: #fff; }
2671
+ .card:hover { border-color: #c8a43a; }
2672
+ .card-header { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
2673
+ .method-name { font-family: 'JetBrains Mono', monospace; font-size: 1.1em; font-weight: 600;
2674
+ color: #2d5016; }
2675
+ .badge { display: inline-block; padding: 2px 8px; border-radius: 4px;
2676
+ font-size: 0.75em; font-weight: 600; text-transform: uppercase;
2677
+ letter-spacing: 0.03em; }
2678
+ .badge-unary { background: #e8f5e0; color: #2d5016; }
2679
+ .badge-stream { background: #e0ecf5; color: #1a4a6b; }
2680
+ .badge-exchange { background: #f5e6f0; color: #6b234a; }
2681
+ .badge-producer { background: #e0f0f5; color: #1a5a6b; }
2682
+ .badge-header { background: #f5eee0; color: #6b4423; }
2683
+ .docstring { color: #6b6b5a; font-size: 0.9em; margin-top: 0; }
2684
+ table { width: 100%; border-collapse: collapse; font-size: 0.9em; }
2685
+ th { text-align: left; padding: 8px 10px; background: #f0ece0; color: #2c2c1e;
2686
+ font-weight: 600; border-bottom: 2px solid #e0dcd0; }
2687
+ td { padding: 8px 10px; border-bottom: 1px solid #f0ece0; }
2688
+ td code { font-size: 0.85em; }
2689
+ .no-params { color: #6b6b5a; font-style: italic; font-size: 0.9em; }
2690
+ .section-label { font-size: 0.8em; font-weight: 600; text-transform: uppercase;
2691
+ letter-spacing: 0.05em; color: #6b6b5a; margin-top: 14px;
2692
+ margin-bottom: 6px; }
2693
+ footer { text-align: center; margin-top: 48px; padding: 20px 0;
2694
+ border-top: 1px solid #f0ece0; color: #6b6b5a; font-size: 0.85em; }
2695
+ footer a { color: #2d5016; font-weight: 600; }
2696
+ footer a:hover { color: #4a7c23; }
2697
+ </style>
2698
+ </head>
2699
+ <body>
2700
+ <div class="header">
2701
+ <div class="logo">
2702
+ <img src="${LOGO_URL}" alt="vgi-rpc logo">
2703
+ </div>
2704
+ <h1>${escapeHtml(protocolName)}</h1>
2705
+ <p class="subtitle">API Reference</p>
2706
+ <p class="meta">Powered by <code>vgi-rpc</code> (TypeScript) &middot; server <code>${escapeHtml(serverId)}</code>${repoLink}</p>
2707
+ </div>
2708
+ ${cards}
2709
+ <footer>
2710
+ <a href="https://vgi-rpc.query.farm">Learn more about <code>vgi-rpc</code></a>
2711
+ &middot;
2712
+ &copy; 2026 &#x1F69C; <a href="https://query.farm">Query.Farm LLC</a>
2713
+ </footer>
2714
+ </body>
2715
+ </html>`;
2716
+ }
2717
+
1997
2718
  // src/http/types.ts
1998
2719
  var jsonStateSerializer = {
1999
2720
  serialize(state) {
@@ -2007,22 +2728,35 @@ var jsonStateSerializer = {
2007
2728
  // src/http/handler.ts
2008
2729
  var EMPTY_SCHEMA2 = new Schema5([]);
2009
2730
  function createHttpHandler(protocol, options) {
2010
- const prefix = (options?.prefix ?? "/vgi").replace(/\/+$/, "");
2731
+ const prefix = (options?.prefix ?? "").replace(/\/+$/, "");
2011
2732
  const signingKey = options?.signingKey ?? randomBytes(32);
2012
2733
  const tokenTtl = options?.tokenTtl ?? 3600;
2013
2734
  const corsOrigins = options?.corsOrigins;
2014
2735
  const maxRequestBytes = options?.maxRequestBytes;
2015
2736
  const maxStreamResponseBytes = options?.maxStreamResponseBytes;
2016
2737
  const serverId = options?.serverId ?? crypto.randomUUID().replace(/-/g, "").slice(0, 12);
2738
+ const authenticate = options?.authenticate;
2739
+ const oauthMetadata = options?.oauthResourceMetadata;
2017
2740
  const methods = protocol.getMethods();
2018
2741
  const compressionLevel = options?.compressionLevel;
2019
2742
  const stateSerializer = options?.stateSerializer ?? jsonStateSerializer;
2020
- const ctx = {
2743
+ const dispatchHook = options?.dispatchHook;
2744
+ const enableLandingPage = options?.enableLandingPage ?? true;
2745
+ const enableDescribePage = options?.enableDescribePage ?? true;
2746
+ const enableNotFoundPage = options?.enableNotFoundPage ?? true;
2747
+ const displayName = options?.protocolName ?? protocol.name;
2748
+ const repoUrl = options?.repositoryUrl ?? null;
2749
+ const landingHtml = enableLandingPage ? buildLandingPage(displayName, serverId, enableDescribePage ? `${prefix}/describe` : null, repoUrl) : null;
2750
+ const describeHtml = enableDescribePage ? buildDescribePage(displayName, serverId, methods, repoUrl) : null;
2751
+ const notFoundHtml = enableNotFoundPage ? buildNotFoundPage(prefix, displayName) : null;
2752
+ const externalLocation = options?.externalLocation;
2753
+ const baseCtx = {
2021
2754
  signingKey,
2022
2755
  tokenTtl,
2023
2756
  serverId,
2024
2757
  maxStreamResponseBytes,
2025
- stateSerializer
2758
+ stateSerializer,
2759
+ externalLocation
2026
2760
  };
2027
2761
  function addCorsHeaders(headers) {
2028
2762
  if (corsOrigins) {
@@ -2053,6 +2787,18 @@ function createHttpHandler(protocol, options) {
2053
2787
  return async function handler(request) {
2054
2788
  const url = new URL(request.url);
2055
2789
  const path = url.pathname;
2790
+ if (oauthMetadata && path === wellKnownPath(prefix)) {
2791
+ if (request.method !== "GET") {
2792
+ return new Response("Method Not Allowed", { status: 405 });
2793
+ }
2794
+ const body2 = JSON.stringify(oauthResourceMetadataToJson(oauthMetadata));
2795
+ const headers = new Headers({
2796
+ "Content-Type": "application/json",
2797
+ "Cache-Control": "public, max-age=60"
2798
+ });
2799
+ addCorsHeaders(headers);
2800
+ return new Response(body2, { status: 200, headers });
2801
+ }
2056
2802
  if (request.method === "OPTIONS") {
2057
2803
  if (path === `${prefix}/__capabilities__`) {
2058
2804
  const headers = new Headers;
@@ -2069,6 +2815,24 @@ function createHttpHandler(protocol, options) {
2069
2815
  }
2070
2816
  return new Response(null, { status: 405 });
2071
2817
  }
2818
+ if (request.method === "GET") {
2819
+ if (landingHtml && (path === prefix || path === `${prefix}/`)) {
2820
+ const headers = new Headers({ "Content-Type": "text/html; charset=utf-8" });
2821
+ addCorsHeaders(headers);
2822
+ return new Response(landingHtml, { status: 200, headers });
2823
+ }
2824
+ if (describeHtml && path === `${prefix}/describe`) {
2825
+ const headers = new Headers({ "Content-Type": "text/html; charset=utf-8" });
2826
+ addCorsHeaders(headers);
2827
+ return new Response(describeHtml, { status: 200, headers });
2828
+ }
2829
+ if (notFoundHtml) {
2830
+ const headers = new Headers({ "Content-Type": "text/html; charset=utf-8" });
2831
+ addCorsHeaders(headers);
2832
+ return new Response(notFoundHtml, { status: 404, headers });
2833
+ }
2834
+ return new Response("Not Found", { status: 404 });
2835
+ }
2072
2836
  if (request.method !== "POST") {
2073
2837
  return new Response("Method Not Allowed", { status: 405 });
2074
2838
  }
@@ -2088,6 +2852,22 @@ function createHttpHandler(protocol, options) {
2088
2852
  if (contentEncoding === "zstd") {
2089
2853
  body = zstdDecompress(body);
2090
2854
  }
2855
+ const ctx = { ...baseCtx };
2856
+ if (authenticate) {
2857
+ try {
2858
+ ctx.authContext = await authenticate(request);
2859
+ } catch (error) {
2860
+ const headers = new Headers({ "Content-Type": "text/plain" });
2861
+ addCorsHeaders(headers);
2862
+ if (oauthMetadata) {
2863
+ const metadataUrl = new URL(request.url);
2864
+ metadataUrl.pathname = wellKnownPath(prefix);
2865
+ metadataUrl.search = "";
2866
+ headers.set("WWW-Authenticate", buildWwwAuthenticateHeader(metadataUrl.toString(), oauthMetadata.clientId, oauthMetadata.clientSecret, oauthMetadata.useIdTokenAsBearer, oauthMetadata.deviceCodeClientId, oauthMetadata.deviceCodeClientSecret));
2867
+ }
2868
+ return new Response(error.message || "Unauthorized", { status: 401, headers });
2869
+ }
2870
+ }
2091
2871
  if (path === `${prefix}/${DESCRIBE_METHOD_NAME}`) {
2092
2872
  try {
2093
2873
  const response = httpDispatchDescribe(protocol.name, methods, serverId);
@@ -2119,6 +2899,18 @@ function createHttpHandler(protocol, options) {
2119
2899
  const err = new Error(`Unknown method: '${methodName}'. Available methods: [${available.join(", ")}]`);
2120
2900
  return compressIfAccepted(makeErrorResponse(err, 404), clientAcceptsZstd);
2121
2901
  }
2902
+ const methodType = method.type === "unary" /* UNARY */ ? "unary" : "stream";
2903
+ const info = { method: methodName, methodType, serverId, requestId: null };
2904
+ const stats = {
2905
+ inputBatches: 0,
2906
+ outputBatches: 0,
2907
+ inputRows: 0,
2908
+ outputRows: 0,
2909
+ inputBytes: 0,
2910
+ outputBytes: 0
2911
+ };
2912
+ const hookToken = dispatchHook?.onDispatchStart(info);
2913
+ let dispatchError;
2122
2914
  try {
2123
2915
  let response;
2124
2916
  if (action === "call") {
@@ -2137,100 +2929,1510 @@ function createHttpHandler(protocol, options) {
2137
2929
  }
2138
2930
  response = await httpDispatchStreamExchange(method, body, ctx);
2139
2931
  }
2932
+ const internalError = response.__dispatchError;
2933
+ if (internalError) {
2934
+ dispatchError = internalError instanceof Error ? internalError : new Error(String(internalError));
2935
+ }
2140
2936
  addCorsHeaders(response.headers);
2141
2937
  return compressIfAccepted(response, clientAcceptsZstd);
2142
2938
  } catch (error) {
2939
+ dispatchError = error instanceof Error ? error : new Error(String(error));
2143
2940
  if (error instanceof HttpRpcError) {
2144
2941
  return compressIfAccepted(makeErrorResponse(error, error.statusCode), clientAcceptsZstd);
2145
2942
  }
2146
2943
  return compressIfAccepted(makeErrorResponse(error, 500), clientAcceptsZstd);
2944
+ } finally {
2945
+ dispatchHook?.onDispatchEnd(hookToken, info, stats, dispatchError);
2147
2946
  }
2148
2947
  };
2149
2948
  }
2150
- // src/protocol.ts
2151
- import { Schema as Schema7 } from "@query-farm/apache-arrow";
2152
-
2153
- // src/schema.ts
2154
- import {
2155
- Binary as Binary3,
2156
- Bool as Bool3,
2157
- DataType as DataType4,
2158
- Field as Field4,
2159
- Float32,
2160
- Float64 as Float642,
2161
- Int16,
2162
- Int32,
2163
- Int64 as Int642,
2164
- Schema as Schema6,
2165
- Utf8 as Utf83
2166
- } from "@query-farm/apache-arrow";
2167
- var str = new Utf83;
2168
- var bytes = new Binary3;
2169
- var int = new Int642;
2170
- var int32 = new Int32;
2171
- var float = new Float642;
2172
- var float32 = new Float32;
2173
- var bool = new Bool3;
2174
- function toSchema(spec) {
2175
- if (spec instanceof Schema6)
2176
- return spec;
2177
- const fields = [];
2178
- for (const [name, value] of Object.entries(spec)) {
2179
- if (value instanceof Field4) {
2180
- fields.push(value);
2181
- } else if (value instanceof DataType4) {
2182
- fields.push(new Field4(name, value, false));
2183
- } else {
2184
- throw new TypeError(`Invalid schema value for "${name}": expected DataType or Field, got ${typeof value}`);
2185
- }
2949
+ // node_modules/oauth4webapi/build/index.js
2950
+ var USER_AGENT;
2951
+ if (typeof navigator === "undefined" || !navigator.userAgent?.startsWith?.("Mozilla/5.0 ")) {
2952
+ const NAME = "oauth4webapi";
2953
+ const VERSION = "v3.8.5";
2954
+ USER_AGENT = `${NAME}/${VERSION}`;
2955
+ }
2956
+ function looseInstanceOf(input, expected) {
2957
+ if (input == null) {
2958
+ return false;
2959
+ }
2960
+ try {
2961
+ return input instanceof expected || Object.getPrototypeOf(input)[Symbol.toStringTag] === expected.prototype[Symbol.toStringTag];
2962
+ } catch {
2963
+ return false;
2186
2964
  }
2187
- return new Schema6(fields);
2188
2965
  }
2189
- var TYPE_MAP = [
2190
- [Utf83, "str"],
2191
- [Binary3, "bytes"],
2192
- [Bool3, "bool"],
2193
- [Float642, "float"],
2194
- [Float32, "float"],
2195
- [Int642, "int"],
2196
- [Int32, "int"],
2197
- [Int16, "int"]
2198
- ];
2199
- function inferParamTypes(spec) {
2200
- const schema = toSchema(spec);
2201
- if (schema.fields.length === 0)
2202
- return;
2203
- const result = {};
2204
- for (const field of schema.fields) {
2205
- let mapped;
2206
- for (const [ctor, name] of TYPE_MAP) {
2207
- if (field.type instanceof ctor) {
2208
- mapped = name;
2209
- break;
2966
+ var ERR_INVALID_ARG_VALUE = "ERR_INVALID_ARG_VALUE";
2967
+ var ERR_INVALID_ARG_TYPE = "ERR_INVALID_ARG_TYPE";
2968
+ function CodedTypeError(message, code, cause) {
2969
+ const err = new TypeError(message, { cause });
2970
+ Object.assign(err, { code });
2971
+ return err;
2972
+ }
2973
+ var allowInsecureRequests = Symbol();
2974
+ var clockSkew = Symbol();
2975
+ var clockTolerance = Symbol();
2976
+ var customFetch = Symbol();
2977
+ var modifyAssertion = Symbol();
2978
+ var jweDecrypt = Symbol();
2979
+ var jwksCache = Symbol();
2980
+ var encoder = new TextEncoder;
2981
+ var decoder = new TextDecoder;
2982
+ function buf(input) {
2983
+ if (typeof input === "string") {
2984
+ return encoder.encode(input);
2985
+ }
2986
+ return decoder.decode(input);
2987
+ }
2988
+ var encodeBase64Url;
2989
+ if (Uint8Array.prototype.toBase64) {
2990
+ encodeBase64Url = (input) => {
2991
+ if (input instanceof ArrayBuffer) {
2992
+ input = new Uint8Array(input);
2993
+ }
2994
+ return input.toBase64({ alphabet: "base64url", omitPadding: true });
2995
+ };
2996
+ } else {
2997
+ const CHUNK_SIZE = 32768;
2998
+ encodeBase64Url = (input) => {
2999
+ if (input instanceof ArrayBuffer) {
3000
+ input = new Uint8Array(input);
3001
+ }
3002
+ const arr = [];
3003
+ for (let i = 0;i < input.byteLength; i += CHUNK_SIZE) {
3004
+ arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));
3005
+ }
3006
+ return btoa(arr.join("")).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
3007
+ };
3008
+ }
3009
+ var decodeBase64Url;
3010
+ if (Uint8Array.fromBase64) {
3011
+ decodeBase64Url = (input) => {
3012
+ try {
3013
+ return Uint8Array.fromBase64(input, { alphabet: "base64url" });
3014
+ } catch (cause) {
3015
+ throw CodedTypeError("The input to be decoded is not correctly encoded.", ERR_INVALID_ARG_VALUE, cause);
3016
+ }
3017
+ };
3018
+ } else {
3019
+ decodeBase64Url = (input) => {
3020
+ try {
3021
+ const binary = atob(input.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, ""));
3022
+ const bytes = new Uint8Array(binary.length);
3023
+ for (let i = 0;i < binary.length; i++) {
3024
+ bytes[i] = binary.charCodeAt(i);
2210
3025
  }
3026
+ return bytes;
3027
+ } catch (cause) {
3028
+ throw CodedTypeError("The input to be decoded is not correctly encoded.", ERR_INVALID_ARG_VALUE, cause);
2211
3029
  }
2212
- if (!mapped)
2213
- return;
2214
- result[field.name] = mapped;
3030
+ };
3031
+ }
3032
+ function b64u(input) {
3033
+ if (typeof input === "string") {
3034
+ return decodeBase64Url(input);
2215
3035
  }
2216
- return result;
3036
+ return encodeBase64Url(input);
2217
3037
  }
2218
3038
 
2219
- // src/protocol.ts
2220
- var EMPTY_SCHEMA3 = new Schema7([]);
3039
+ class UnsupportedOperationError extends Error {
3040
+ code;
3041
+ constructor(message, options) {
3042
+ super(message, options);
3043
+ this.name = this.constructor.name;
3044
+ this.code = UNSUPPORTED_OPERATION;
3045
+ Error.captureStackTrace?.(this, this.constructor);
3046
+ }
3047
+ }
2221
3048
 
2222
- class Protocol {
2223
- name;
2224
- _methods = new Map;
2225
- constructor(name) {
2226
- this.name = name;
3049
+ class OperationProcessingError extends Error {
3050
+ code;
3051
+ constructor(message, options) {
3052
+ super(message, options);
3053
+ this.name = this.constructor.name;
3054
+ if (options?.code) {
3055
+ this.code = options?.code;
3056
+ }
3057
+ Error.captureStackTrace?.(this, this.constructor);
2227
3058
  }
2228
- unary(name, config) {
2229
- const params = toSchema(config.params);
2230
- this._methods.set(name, {
2231
- name,
2232
- type: "unary" /* UNARY */,
2233
- paramsSchema: params,
3059
+ }
3060
+ function OPE(message, code, cause) {
3061
+ return new OperationProcessingError(message, { code, cause });
3062
+ }
3063
+ async function calculateJwkThumbprint(jwk) {
3064
+ let components;
3065
+ switch (jwk.kty) {
3066
+ case "EC":
3067
+ components = {
3068
+ crv: jwk.crv,
3069
+ kty: jwk.kty,
3070
+ x: jwk.x,
3071
+ y: jwk.y
3072
+ };
3073
+ break;
3074
+ case "OKP":
3075
+ components = {
3076
+ crv: jwk.crv,
3077
+ kty: jwk.kty,
3078
+ x: jwk.x
3079
+ };
3080
+ break;
3081
+ case "AKP":
3082
+ components = {
3083
+ alg: jwk.alg,
3084
+ kty: jwk.kty,
3085
+ pub: jwk.pub
3086
+ };
3087
+ break;
3088
+ case "RSA":
3089
+ components = {
3090
+ e: jwk.e,
3091
+ kty: jwk.kty,
3092
+ n: jwk.n
3093
+ };
3094
+ break;
3095
+ default:
3096
+ throw new UnsupportedOperationError("unsupported JWK key type", { cause: jwk });
3097
+ }
3098
+ return b64u(await crypto.subtle.digest("SHA-256", buf(JSON.stringify(components))));
3099
+ }
3100
+ function assertCryptoKey(key, it) {
3101
+ if (!(key instanceof CryptoKey)) {
3102
+ throw CodedTypeError(`${it} must be a CryptoKey`, ERR_INVALID_ARG_TYPE);
3103
+ }
3104
+ }
3105
+ function assertPrivateKey(key, it) {
3106
+ assertCryptoKey(key, it);
3107
+ if (key.type !== "private") {
3108
+ throw CodedTypeError(`${it} must be a private CryptoKey`, ERR_INVALID_ARG_VALUE);
3109
+ }
3110
+ }
3111
+ function assertPublicKey(key, it) {
3112
+ assertCryptoKey(key, it);
3113
+ if (key.type !== "public") {
3114
+ throw CodedTypeError(`${it} must be a public CryptoKey`, ERR_INVALID_ARG_VALUE);
3115
+ }
3116
+ }
3117
+ function normalizeTyp(value) {
3118
+ return value.toLowerCase().replace(/^application\//, "");
3119
+ }
3120
+ function isJsonObject(input) {
3121
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
3122
+ return false;
3123
+ }
3124
+ return true;
3125
+ }
3126
+ function prepareHeaders(input) {
3127
+ if (looseInstanceOf(input, Headers)) {
3128
+ input = Object.fromEntries(input.entries());
3129
+ }
3130
+ const headers = new Headers(input ?? {});
3131
+ if (USER_AGENT && !headers.has("user-agent")) {
3132
+ headers.set("user-agent", USER_AGENT);
3133
+ }
3134
+ if (headers.has("authorization")) {
3135
+ throw CodedTypeError('"options.headers" must not include the "authorization" header name', ERR_INVALID_ARG_VALUE);
3136
+ }
3137
+ return headers;
3138
+ }
3139
+ function signal(url, value) {
3140
+ if (value !== undefined) {
3141
+ if (typeof value === "function") {
3142
+ value = value(url.href);
3143
+ }
3144
+ if (!(value instanceof AbortSignal)) {
3145
+ throw CodedTypeError('"options.signal" must return or be an instance of AbortSignal', ERR_INVALID_ARG_TYPE);
3146
+ }
3147
+ return value;
3148
+ }
3149
+ return;
3150
+ }
3151
+ function replaceDoubleSlash(pathname) {
3152
+ if (pathname.includes("//")) {
3153
+ return pathname.replace("//", "/");
3154
+ }
3155
+ return pathname;
3156
+ }
3157
+ function prependWellKnown(url, wellKnown, allowTerminatingSlash = false) {
3158
+ if (url.pathname === "/") {
3159
+ url.pathname = wellKnown;
3160
+ } else {
3161
+ url.pathname = replaceDoubleSlash(`${wellKnown}/${allowTerminatingSlash ? url.pathname : url.pathname.replace(/(\/)$/, "")}`);
3162
+ }
3163
+ return url;
3164
+ }
3165
+ function appendWellKnown(url, wellKnown) {
3166
+ url.pathname = replaceDoubleSlash(`${url.pathname}/${wellKnown}`);
3167
+ return url;
3168
+ }
3169
+ async function performDiscovery(input, urlName, transform, options) {
3170
+ if (!(input instanceof URL)) {
3171
+ throw CodedTypeError(`"${urlName}" must be an instance of URL`, ERR_INVALID_ARG_TYPE);
3172
+ }
3173
+ checkProtocol(input, options?.[allowInsecureRequests] !== true);
3174
+ const url = transform(new URL(input.href));
3175
+ const headers = prepareHeaders(options?.headers);
3176
+ headers.set("accept", "application/json");
3177
+ return (options?.[customFetch] || fetch)(url.href, {
3178
+ body: undefined,
3179
+ headers: Object.fromEntries(headers.entries()),
3180
+ method: "GET",
3181
+ redirect: "manual",
3182
+ signal: signal(url, options?.signal)
3183
+ });
3184
+ }
3185
+ async function discoveryRequest(issuerIdentifier, options) {
3186
+ return performDiscovery(issuerIdentifier, "issuerIdentifier", (url) => {
3187
+ switch (options?.algorithm) {
3188
+ case undefined:
3189
+ case "oidc":
3190
+ appendWellKnown(url, ".well-known/openid-configuration");
3191
+ break;
3192
+ case "oauth2":
3193
+ prependWellKnown(url, ".well-known/oauth-authorization-server");
3194
+ break;
3195
+ default:
3196
+ throw CodedTypeError('"options.algorithm" must be "oidc" (default), or "oauth2"', ERR_INVALID_ARG_VALUE);
3197
+ }
3198
+ return url;
3199
+ }, options);
3200
+ }
3201
+ function assertString(input, it, code, cause) {
3202
+ try {
3203
+ if (typeof input !== "string") {
3204
+ throw CodedTypeError(`${it} must be a string`, ERR_INVALID_ARG_TYPE, cause);
3205
+ }
3206
+ if (input.length === 0) {
3207
+ throw CodedTypeError(`${it} must not be empty`, ERR_INVALID_ARG_VALUE, cause);
3208
+ }
3209
+ } catch (err) {
3210
+ if (code) {
3211
+ throw OPE(err.message, code, cause);
3212
+ }
3213
+ throw err;
3214
+ }
3215
+ }
3216
+ async function processDiscoveryResponse(expectedIssuerIdentifier, response) {
3217
+ const expected = expectedIssuerIdentifier;
3218
+ if (!(expected instanceof URL) && expected !== _nodiscoverycheck) {
3219
+ throw CodedTypeError('"expectedIssuerIdentifier" must be an instance of URL', ERR_INVALID_ARG_TYPE);
3220
+ }
3221
+ if (!looseInstanceOf(response, Response)) {
3222
+ throw CodedTypeError('"response" must be an instance of Response', ERR_INVALID_ARG_TYPE);
3223
+ }
3224
+ if (response.status !== 200) {
3225
+ throw OPE('"response" is not a conform Authorization Server Metadata response (unexpected HTTP status code)', RESPONSE_IS_NOT_CONFORM, response);
3226
+ }
3227
+ assertReadableResponse(response);
3228
+ const json = await getResponseJsonBody(response);
3229
+ assertString(json.issuer, '"response" body "issuer" property', INVALID_RESPONSE, { body: json });
3230
+ if (expected !== _nodiscoverycheck && new URL(json.issuer).href !== expected.href) {
3231
+ throw OPE('"response" body "issuer" property does not match the expected value', JSON_ATTRIBUTE_COMPARISON, { expected: expected.href, body: json, attribute: "issuer" });
3232
+ }
3233
+ return json;
3234
+ }
3235
+ function assertApplicationJson(response) {
3236
+ assertContentType(response, "application/json");
3237
+ }
3238
+ function notJson(response, ...types) {
3239
+ let msg = '"response" content-type must be ';
3240
+ if (types.length > 2) {
3241
+ const last = types.pop();
3242
+ msg += `${types.join(", ")}, or ${last}`;
3243
+ } else if (types.length === 2) {
3244
+ msg += `${types[0]} or ${types[1]}`;
3245
+ } else {
3246
+ msg += types[0];
3247
+ }
3248
+ return OPE(msg, RESPONSE_IS_NOT_JSON, response);
3249
+ }
3250
+ function assertContentTypes(response, ...types) {
3251
+ if (!types.includes(getContentType(response))) {
3252
+ throw notJson(response, ...types);
3253
+ }
3254
+ }
3255
+ function assertContentType(response, contentType) {
3256
+ if (getContentType(response) !== contentType) {
3257
+ throw notJson(response, contentType);
3258
+ }
3259
+ }
3260
+ function randomBytes2() {
3261
+ return b64u(crypto.getRandomValues(new Uint8Array(32)));
3262
+ }
3263
+ function psAlg(key) {
3264
+ switch (key.algorithm.hash.name) {
3265
+ case "SHA-256":
3266
+ return "PS256";
3267
+ case "SHA-384":
3268
+ return "PS384";
3269
+ case "SHA-512":
3270
+ return "PS512";
3271
+ default:
3272
+ throw new UnsupportedOperationError("unsupported RsaHashedKeyAlgorithm hash name", {
3273
+ cause: key
3274
+ });
3275
+ }
3276
+ }
3277
+ function rsAlg(key) {
3278
+ switch (key.algorithm.hash.name) {
3279
+ case "SHA-256":
3280
+ return "RS256";
3281
+ case "SHA-384":
3282
+ return "RS384";
3283
+ case "SHA-512":
3284
+ return "RS512";
3285
+ default:
3286
+ throw new UnsupportedOperationError("unsupported RsaHashedKeyAlgorithm hash name", {
3287
+ cause: key
3288
+ });
3289
+ }
3290
+ }
3291
+ function esAlg(key) {
3292
+ switch (key.algorithm.namedCurve) {
3293
+ case "P-256":
3294
+ return "ES256";
3295
+ case "P-384":
3296
+ return "ES384";
3297
+ case "P-521":
3298
+ return "ES512";
3299
+ default:
3300
+ throw new UnsupportedOperationError("unsupported EcKeyAlgorithm namedCurve", { cause: key });
3301
+ }
3302
+ }
3303
+ function keyToJws(key) {
3304
+ switch (key.algorithm.name) {
3305
+ case "RSA-PSS":
3306
+ return psAlg(key);
3307
+ case "RSASSA-PKCS1-v1_5":
3308
+ return rsAlg(key);
3309
+ case "ECDSA":
3310
+ return esAlg(key);
3311
+ case "Ed25519":
3312
+ case "ML-DSA-44":
3313
+ case "ML-DSA-65":
3314
+ case "ML-DSA-87":
3315
+ return key.algorithm.name;
3316
+ case "EdDSA":
3317
+ return "Ed25519";
3318
+ default:
3319
+ throw new UnsupportedOperationError("unsupported CryptoKey algorithm name", { cause: key });
3320
+ }
3321
+ }
3322
+ function getClockSkew(client) {
3323
+ const skew = client?.[clockSkew];
3324
+ return typeof skew === "number" && Number.isFinite(skew) ? skew : 0;
3325
+ }
3326
+ function getClockTolerance(client) {
3327
+ const tolerance = client?.[clockTolerance];
3328
+ return typeof tolerance === "number" && Number.isFinite(tolerance) && Math.sign(tolerance) !== -1 ? tolerance : 30;
3329
+ }
3330
+ function epochTime() {
3331
+ return Math.floor(Date.now() / 1000);
3332
+ }
3333
+ function assertAs(as) {
3334
+ if (typeof as !== "object" || as === null) {
3335
+ throw CodedTypeError('"as" must be an object', ERR_INVALID_ARG_TYPE);
3336
+ }
3337
+ assertString(as.issuer, '"as.issuer"');
3338
+ }
3339
+ async function signJwt(header, payload, key) {
3340
+ if (!key.usages.includes("sign")) {
3341
+ throw CodedTypeError('CryptoKey instances used for signing assertions must include "sign" in their "usages"', ERR_INVALID_ARG_VALUE);
3342
+ }
3343
+ const input = `${b64u(buf(JSON.stringify(header)))}.${b64u(buf(JSON.stringify(payload)))}`;
3344
+ const signature = b64u(await crypto.subtle.sign(keyToSubtle(key), key, buf(input)));
3345
+ return `${input}.${signature}`;
3346
+ }
3347
+ var jwkCache;
3348
+ async function getSetPublicJwkCache(key, alg) {
3349
+ const { kty, e, n, x, y, crv, pub } = await crypto.subtle.exportKey("jwk", key);
3350
+ const jwk = { kty, e, n, x, y, crv, pub };
3351
+ if (kty === "AKP")
3352
+ jwk.alg = alg;
3353
+ jwkCache.set(key, jwk);
3354
+ return jwk;
3355
+ }
3356
+ async function publicJwk(key, alg) {
3357
+ jwkCache ||= new WeakMap;
3358
+ return jwkCache.get(key) || getSetPublicJwkCache(key, alg);
3359
+ }
3360
+ var URLParse = URL.parse ? (url, base) => URL.parse(url, base) : (url, base) => {
3361
+ try {
3362
+ return new URL(url, base);
3363
+ } catch {
3364
+ return null;
3365
+ }
3366
+ };
3367
+ function checkProtocol(url, enforceHttps) {
3368
+ if (enforceHttps && url.protocol !== "https:") {
3369
+ throw OPE("only requests to HTTPS are allowed", HTTP_REQUEST_FORBIDDEN, url);
3370
+ }
3371
+ if (url.protocol !== "https:" && url.protocol !== "http:") {
3372
+ throw OPE("only HTTP and HTTPS requests are allowed", REQUEST_PROTOCOL_FORBIDDEN, url);
3373
+ }
3374
+ }
3375
+ function validateEndpoint(value, endpoint, useMtlsAlias, enforceHttps) {
3376
+ let url;
3377
+ if (typeof value !== "string" || !(url = URLParse(value))) {
3378
+ throw OPE(`authorization server metadata does not contain a valid ${useMtlsAlias ? `"as.mtls_endpoint_aliases.${endpoint}"` : `"as.${endpoint}"`}`, value === undefined ? MISSING_SERVER_METADATA : INVALID_SERVER_METADATA, { attribute: useMtlsAlias ? `mtls_endpoint_aliases.${endpoint}` : endpoint });
3379
+ }
3380
+ checkProtocol(url, enforceHttps);
3381
+ return url;
3382
+ }
3383
+ function resolveEndpoint(as, endpoint, useMtlsAlias, enforceHttps) {
3384
+ if (useMtlsAlias && as.mtls_endpoint_aliases && endpoint in as.mtls_endpoint_aliases) {
3385
+ return validateEndpoint(as.mtls_endpoint_aliases[endpoint], endpoint, useMtlsAlias, enforceHttps);
3386
+ }
3387
+ return validateEndpoint(as[endpoint], endpoint, useMtlsAlias, enforceHttps);
3388
+ }
3389
+ class DPoPHandler {
3390
+ #header;
3391
+ #privateKey;
3392
+ #publicKey;
3393
+ #clockSkew;
3394
+ #modifyAssertion;
3395
+ #map;
3396
+ #jkt;
3397
+ constructor(client, keyPair, options) {
3398
+ assertPrivateKey(keyPair?.privateKey, '"DPoP.privateKey"');
3399
+ assertPublicKey(keyPair?.publicKey, '"DPoP.publicKey"');
3400
+ if (!keyPair.publicKey.extractable) {
3401
+ throw CodedTypeError('"DPoP.publicKey.extractable" must be true', ERR_INVALID_ARG_VALUE);
3402
+ }
3403
+ this.#modifyAssertion = options?.[modifyAssertion];
3404
+ this.#clockSkew = getClockSkew(client);
3405
+ this.#privateKey = keyPair.privateKey;
3406
+ this.#publicKey = keyPair.publicKey;
3407
+ branded.add(this);
3408
+ }
3409
+ #get(key) {
3410
+ this.#map ||= new Map;
3411
+ let item = this.#map.get(key);
3412
+ if (item) {
3413
+ this.#map.delete(key);
3414
+ this.#map.set(key, item);
3415
+ }
3416
+ return item;
3417
+ }
3418
+ #set(key, val) {
3419
+ this.#map ||= new Map;
3420
+ this.#map.delete(key);
3421
+ if (this.#map.size === 100) {
3422
+ this.#map.delete(this.#map.keys().next().value);
3423
+ }
3424
+ this.#map.set(key, val);
3425
+ }
3426
+ async calculateThumbprint() {
3427
+ if (!this.#jkt) {
3428
+ const jwk = await crypto.subtle.exportKey("jwk", this.#publicKey);
3429
+ this.#jkt ||= await calculateJwkThumbprint(jwk);
3430
+ }
3431
+ return this.#jkt;
3432
+ }
3433
+ async addProof(url, headers, htm, accessToken) {
3434
+ const alg = keyToJws(this.#privateKey);
3435
+ this.#header ||= {
3436
+ alg,
3437
+ typ: "dpop+jwt",
3438
+ jwk: await publicJwk(this.#publicKey, alg)
3439
+ };
3440
+ const nonce = this.#get(url.origin);
3441
+ const now = epochTime() + this.#clockSkew;
3442
+ const payload = {
3443
+ iat: now,
3444
+ jti: randomBytes2(),
3445
+ htm,
3446
+ nonce,
3447
+ htu: `${url.origin}${url.pathname}`,
3448
+ ath: accessToken ? b64u(await crypto.subtle.digest("SHA-256", buf(accessToken))) : undefined
3449
+ };
3450
+ this.#modifyAssertion?.(this.#header, payload);
3451
+ headers.set("dpop", await signJwt(this.#header, payload, this.#privateKey));
3452
+ }
3453
+ cacheNonce(response, url) {
3454
+ try {
3455
+ const nonce = response.headers.get("dpop-nonce");
3456
+ if (nonce) {
3457
+ this.#set(url.origin, nonce);
3458
+ }
3459
+ } catch {}
3460
+ }
3461
+ }
3462
+ var tokenMatch = "[a-zA-Z0-9!#$%&\\'\\*\\+\\-\\.\\^_`\\|~]+";
3463
+ var token68Match = "[a-zA-Z0-9\\-\\._\\~\\+\\/]+={0,2}";
3464
+ var quotedMatch = '"((?:[^"\\\\]|\\\\[\\s\\S])*)"';
3465
+ var quotedParamMatcher = "(" + tokenMatch + ")\\s*=\\s*" + quotedMatch;
3466
+ var paramMatcher = "(" + tokenMatch + ")\\s*=\\s*(" + tokenMatch + ")";
3467
+ var schemeRE = new RegExp("^[,\\s]*(" + tokenMatch + ")");
3468
+ var quotedParamRE = new RegExp("^[,\\s]*" + quotedParamMatcher + "[,\\s]*(.*)");
3469
+ var unquotedParamRE = new RegExp("^[,\\s]*" + paramMatcher + "[,\\s]*(.*)");
3470
+ var token68ParamRE = new RegExp("^(" + token68Match + ")(?:$|[,\\s])(.*)");
3471
+ var jwksMap;
3472
+ function setJwksCache(as, jwks, uat, cache) {
3473
+ jwksMap ||= new WeakMap;
3474
+ jwksMap.set(as, {
3475
+ jwks,
3476
+ uat,
3477
+ get age() {
3478
+ return epochTime() - this.uat;
3479
+ }
3480
+ });
3481
+ if (cache) {
3482
+ Object.assign(cache, { jwks: structuredClone(jwks), uat });
3483
+ }
3484
+ }
3485
+ function isFreshJwksCache(input) {
3486
+ if (typeof input !== "object" || input === null) {
3487
+ return false;
3488
+ }
3489
+ if (!("uat" in input) || typeof input.uat !== "number" || epochTime() - input.uat >= 300) {
3490
+ return false;
3491
+ }
3492
+ if (!("jwks" in input) || !isJsonObject(input.jwks) || !Array.isArray(input.jwks.keys) || !Array.prototype.every.call(input.jwks.keys, isJsonObject)) {
3493
+ return false;
3494
+ }
3495
+ return true;
3496
+ }
3497
+ function clearJwksCache(as, cache) {
3498
+ jwksMap?.delete(as);
3499
+ delete cache?.jwks;
3500
+ delete cache?.uat;
3501
+ }
3502
+ async function getPublicSigKeyFromIssuerJwksUri(as, options, header) {
3503
+ const { alg, kid } = header;
3504
+ checkSupportedJwsAlg(header);
3505
+ if (!jwksMap?.has(as) && isFreshJwksCache(options?.[jwksCache])) {
3506
+ setJwksCache(as, options?.[jwksCache].jwks, options?.[jwksCache].uat);
3507
+ }
3508
+ let jwks;
3509
+ let age;
3510
+ if (jwksMap?.has(as)) {
3511
+ ({ jwks, age } = jwksMap.get(as));
3512
+ if (age >= 300) {
3513
+ clearJwksCache(as, options?.[jwksCache]);
3514
+ return getPublicSigKeyFromIssuerJwksUri(as, options, header);
3515
+ }
3516
+ } else {
3517
+ jwks = await jwksRequest(as, options).then(processJwksResponse);
3518
+ age = 0;
3519
+ setJwksCache(as, jwks, epochTime(), options?.[jwksCache]);
3520
+ }
3521
+ let kty;
3522
+ switch (alg.slice(0, 2)) {
3523
+ case "RS":
3524
+ case "PS":
3525
+ kty = "RSA";
3526
+ break;
3527
+ case "ES":
3528
+ kty = "EC";
3529
+ break;
3530
+ case "Ed":
3531
+ kty = "OKP";
3532
+ break;
3533
+ case "ML":
3534
+ kty = "AKP";
3535
+ break;
3536
+ default:
3537
+ throw new UnsupportedOperationError("unsupported JWS algorithm", { cause: { alg } });
3538
+ }
3539
+ const candidates = jwks.keys.filter((jwk2) => {
3540
+ if (jwk2.kty !== kty) {
3541
+ return false;
3542
+ }
3543
+ if (kid !== undefined && kid !== jwk2.kid) {
3544
+ return false;
3545
+ }
3546
+ if (jwk2.alg !== undefined && alg !== jwk2.alg) {
3547
+ return false;
3548
+ }
3549
+ if (jwk2.use !== undefined && jwk2.use !== "sig") {
3550
+ return false;
3551
+ }
3552
+ if (jwk2.key_ops?.includes("verify") === false) {
3553
+ return false;
3554
+ }
3555
+ switch (true) {
3556
+ case (alg === "ES256" && jwk2.crv !== "P-256"):
3557
+ case (alg === "ES384" && jwk2.crv !== "P-384"):
3558
+ case (alg === "ES512" && jwk2.crv !== "P-521"):
3559
+ case (alg === "Ed25519" && jwk2.crv !== "Ed25519"):
3560
+ case (alg === "EdDSA" && jwk2.crv !== "Ed25519"):
3561
+ return false;
3562
+ }
3563
+ return true;
3564
+ });
3565
+ const { 0: jwk, length } = candidates;
3566
+ if (!length) {
3567
+ if (age >= 60) {
3568
+ clearJwksCache(as, options?.[jwksCache]);
3569
+ return getPublicSigKeyFromIssuerJwksUri(as, options, header);
3570
+ }
3571
+ throw OPE("error when selecting a JWT verification key, no applicable keys found", KEY_SELECTION, { header, candidates, jwks_uri: new URL(as.jwks_uri) });
3572
+ }
3573
+ if (length !== 1) {
3574
+ throw OPE('error when selecting a JWT verification key, multiple applicable keys found, a "kid" JWT Header Parameter is required', KEY_SELECTION, { header, candidates, jwks_uri: new URL(as.jwks_uri) });
3575
+ }
3576
+ return importJwk(alg, jwk);
3577
+ }
3578
+ var skipSubjectCheck = Symbol();
3579
+ function getContentType(input) {
3580
+ return input.headers.get("content-type")?.split(";")[0];
3581
+ }
3582
+ var idTokenClaims = new WeakMap;
3583
+ var jwtRefs = new WeakMap;
3584
+ function validateAudience(expected, result) {
3585
+ if (Array.isArray(result.claims.aud)) {
3586
+ if (!result.claims.aud.includes(expected)) {
3587
+ throw OPE('unexpected JWT "aud" (audience) claim value', JWT_CLAIM_COMPARISON, {
3588
+ expected,
3589
+ claims: result.claims,
3590
+ claim: "aud"
3591
+ });
3592
+ }
3593
+ } else if (result.claims.aud !== expected) {
3594
+ throw OPE('unexpected JWT "aud" (audience) claim value', JWT_CLAIM_COMPARISON, {
3595
+ expected,
3596
+ claims: result.claims,
3597
+ claim: "aud"
3598
+ });
3599
+ }
3600
+ return result;
3601
+ }
3602
+ function validateIssuer(as, result) {
3603
+ const expected = as[_expectedIssuer]?.(result) ?? as.issuer;
3604
+ if (result.claims.iss !== expected) {
3605
+ throw OPE('unexpected JWT "iss" (issuer) claim value', JWT_CLAIM_COMPARISON, {
3606
+ expected,
3607
+ claims: result.claims,
3608
+ claim: "iss"
3609
+ });
3610
+ }
3611
+ return result;
3612
+ }
3613
+ var branded = new WeakSet;
3614
+ var nopkce = Symbol();
3615
+ var jwtClaimNames = {
3616
+ aud: "audience",
3617
+ c_hash: "code hash",
3618
+ client_id: "client id",
3619
+ exp: "expiration time",
3620
+ iat: "issued at",
3621
+ iss: "issuer",
3622
+ jti: "jwt id",
3623
+ nonce: "nonce",
3624
+ s_hash: "state hash",
3625
+ sub: "subject",
3626
+ ath: "access token hash",
3627
+ htm: "http method",
3628
+ htu: "http uri",
3629
+ cnf: "confirmation",
3630
+ auth_time: "authentication time"
3631
+ };
3632
+ function validatePresence(required, result) {
3633
+ for (const claim of required) {
3634
+ if (result.claims[claim] === undefined) {
3635
+ throw OPE(`JWT "${claim}" (${jwtClaimNames[claim]}) claim missing`, INVALID_RESPONSE, {
3636
+ claims: result.claims
3637
+ });
3638
+ }
3639
+ }
3640
+ return result;
3641
+ }
3642
+ var expectNoNonce = Symbol();
3643
+ var skipAuthTimeCheck = Symbol();
3644
+ var UNSUPPORTED_OPERATION = "OAUTH_UNSUPPORTED_OPERATION";
3645
+ var PARSE_ERROR = "OAUTH_PARSE_ERROR";
3646
+ var INVALID_RESPONSE = "OAUTH_INVALID_RESPONSE";
3647
+ var INVALID_REQUEST = "OAUTH_INVALID_REQUEST";
3648
+ var RESPONSE_IS_NOT_JSON = "OAUTH_RESPONSE_IS_NOT_JSON";
3649
+ var RESPONSE_IS_NOT_CONFORM = "OAUTH_RESPONSE_IS_NOT_CONFORM";
3650
+ var HTTP_REQUEST_FORBIDDEN = "OAUTH_HTTP_REQUEST_FORBIDDEN";
3651
+ var REQUEST_PROTOCOL_FORBIDDEN = "OAUTH_REQUEST_PROTOCOL_FORBIDDEN";
3652
+ var JWT_TIMESTAMP_CHECK = "OAUTH_JWT_TIMESTAMP_CHECK_FAILED";
3653
+ var JWT_CLAIM_COMPARISON = "OAUTH_JWT_CLAIM_COMPARISON_FAILED";
3654
+ var JSON_ATTRIBUTE_COMPARISON = "OAUTH_JSON_ATTRIBUTE_COMPARISON_FAILED";
3655
+ var KEY_SELECTION = "OAUTH_KEY_SELECTION_FAILED";
3656
+ var MISSING_SERVER_METADATA = "OAUTH_MISSING_SERVER_METADATA";
3657
+ var INVALID_SERVER_METADATA = "OAUTH_INVALID_SERVER_METADATA";
3658
+ function checkJwtType(expected, result) {
3659
+ if (typeof result.header.typ !== "string" || normalizeTyp(result.header.typ) !== expected) {
3660
+ throw OPE('unexpected JWT "typ" header parameter value', INVALID_RESPONSE, {
3661
+ header: result.header
3662
+ });
3663
+ }
3664
+ return result;
3665
+ }
3666
+ function assertReadableResponse(response) {
3667
+ if (response.bodyUsed) {
3668
+ throw CodedTypeError('"response" body has been used already', ERR_INVALID_ARG_VALUE);
3669
+ }
3670
+ }
3671
+ async function jwksRequest(as, options) {
3672
+ assertAs(as);
3673
+ const url = resolveEndpoint(as, "jwks_uri", false, options?.[allowInsecureRequests] !== true);
3674
+ const headers = prepareHeaders(options?.headers);
3675
+ headers.set("accept", "application/json");
3676
+ headers.append("accept", "application/jwk-set+json");
3677
+ return (options?.[customFetch] || fetch)(url.href, {
3678
+ body: undefined,
3679
+ headers: Object.fromEntries(headers.entries()),
3680
+ method: "GET",
3681
+ redirect: "manual",
3682
+ signal: signal(url, options?.signal)
3683
+ });
3684
+ }
3685
+ async function processJwksResponse(response) {
3686
+ if (!looseInstanceOf(response, Response)) {
3687
+ throw CodedTypeError('"response" must be an instance of Response', ERR_INVALID_ARG_TYPE);
3688
+ }
3689
+ if (response.status !== 200) {
3690
+ throw OPE('"response" is not a conform JSON Web Key Set response (unexpected HTTP status code)', RESPONSE_IS_NOT_CONFORM, response);
3691
+ }
3692
+ assertReadableResponse(response);
3693
+ const json = await getResponseJsonBody(response, (response2) => assertContentTypes(response2, "application/json", "application/jwk-set+json"));
3694
+ if (!Array.isArray(json.keys)) {
3695
+ throw OPE('"response" body "keys" property must be an array', INVALID_RESPONSE, { body: json });
3696
+ }
3697
+ if (!Array.prototype.every.call(json.keys, isJsonObject)) {
3698
+ throw OPE('"response" body "keys" property members must be JWK formatted objects', INVALID_RESPONSE, { body: json });
3699
+ }
3700
+ return json;
3701
+ }
3702
+ function supported(alg) {
3703
+ switch (alg) {
3704
+ case "PS256":
3705
+ case "ES256":
3706
+ case "RS256":
3707
+ case "PS384":
3708
+ case "ES384":
3709
+ case "RS384":
3710
+ case "PS512":
3711
+ case "ES512":
3712
+ case "RS512":
3713
+ case "Ed25519":
3714
+ case "EdDSA":
3715
+ case "ML-DSA-44":
3716
+ case "ML-DSA-65":
3717
+ case "ML-DSA-87":
3718
+ return true;
3719
+ default:
3720
+ return false;
3721
+ }
3722
+ }
3723
+ function checkSupportedJwsAlg(header) {
3724
+ if (!supported(header.alg)) {
3725
+ throw new UnsupportedOperationError('unsupported JWS "alg" identifier', {
3726
+ cause: { alg: header.alg }
3727
+ });
3728
+ }
3729
+ }
3730
+ function checkRsaKeyAlgorithm(key) {
3731
+ const { algorithm } = key;
3732
+ if (typeof algorithm.modulusLength !== "number" || algorithm.modulusLength < 2048) {
3733
+ throw new UnsupportedOperationError(`unsupported ${algorithm.name} modulusLength`, {
3734
+ cause: key
3735
+ });
3736
+ }
3737
+ }
3738
+ function ecdsaHashName(key) {
3739
+ const { algorithm } = key;
3740
+ switch (algorithm.namedCurve) {
3741
+ case "P-256":
3742
+ return "SHA-256";
3743
+ case "P-384":
3744
+ return "SHA-384";
3745
+ case "P-521":
3746
+ return "SHA-512";
3747
+ default:
3748
+ throw new UnsupportedOperationError("unsupported ECDSA namedCurve", { cause: key });
3749
+ }
3750
+ }
3751
+ function keyToSubtle(key) {
3752
+ switch (key.algorithm.name) {
3753
+ case "ECDSA":
3754
+ return {
3755
+ name: key.algorithm.name,
3756
+ hash: ecdsaHashName(key)
3757
+ };
3758
+ case "RSA-PSS": {
3759
+ checkRsaKeyAlgorithm(key);
3760
+ switch (key.algorithm.hash.name) {
3761
+ case "SHA-256":
3762
+ case "SHA-384":
3763
+ case "SHA-512":
3764
+ return {
3765
+ name: key.algorithm.name,
3766
+ saltLength: parseInt(key.algorithm.hash.name.slice(-3), 10) >> 3
3767
+ };
3768
+ default:
3769
+ throw new UnsupportedOperationError("unsupported RSA-PSS hash name", { cause: key });
3770
+ }
3771
+ }
3772
+ case "RSASSA-PKCS1-v1_5":
3773
+ checkRsaKeyAlgorithm(key);
3774
+ return key.algorithm.name;
3775
+ case "ML-DSA-44":
3776
+ case "ML-DSA-65":
3777
+ case "ML-DSA-87":
3778
+ case "Ed25519":
3779
+ return key.algorithm.name;
3780
+ }
3781
+ throw new UnsupportedOperationError("unsupported CryptoKey algorithm name", { cause: key });
3782
+ }
3783
+ async function validateJwsSignature(protectedHeader, payload, key, signature) {
3784
+ const data = buf(`${protectedHeader}.${payload}`);
3785
+ const algorithm = keyToSubtle(key);
3786
+ const verified = await crypto.subtle.verify(algorithm, key, signature, data);
3787
+ if (!verified) {
3788
+ throw OPE("JWT signature verification failed", INVALID_RESPONSE, {
3789
+ key,
3790
+ data,
3791
+ signature,
3792
+ algorithm
3793
+ });
3794
+ }
3795
+ }
3796
+ async function validateJwt(jws, checkAlg, clockSkew2, clockTolerance2, decryptJwt) {
3797
+ let { 0: protectedHeader, 1: payload, length } = jws.split(".");
3798
+ if (length === 5) {
3799
+ if (decryptJwt !== undefined) {
3800
+ jws = await decryptJwt(jws);
3801
+ ({ 0: protectedHeader, 1: payload, length } = jws.split("."));
3802
+ } else {
3803
+ throw new UnsupportedOperationError("JWE decryption is not configured", { cause: jws });
3804
+ }
3805
+ }
3806
+ if (length !== 3) {
3807
+ throw OPE("Invalid JWT", INVALID_RESPONSE, jws);
3808
+ }
3809
+ let header;
3810
+ try {
3811
+ header = JSON.parse(buf(b64u(protectedHeader)));
3812
+ } catch (cause) {
3813
+ throw OPE("failed to parse JWT Header body as base64url encoded JSON", PARSE_ERROR, cause);
3814
+ }
3815
+ if (!isJsonObject(header)) {
3816
+ throw OPE("JWT Header must be a top level object", INVALID_RESPONSE, jws);
3817
+ }
3818
+ checkAlg(header);
3819
+ if (header.crit !== undefined) {
3820
+ throw new UnsupportedOperationError('no JWT "crit" header parameter extensions are supported', {
3821
+ cause: { header }
3822
+ });
3823
+ }
3824
+ let claims;
3825
+ try {
3826
+ claims = JSON.parse(buf(b64u(payload)));
3827
+ } catch (cause) {
3828
+ throw OPE("failed to parse JWT Payload body as base64url encoded JSON", PARSE_ERROR, cause);
3829
+ }
3830
+ if (!isJsonObject(claims)) {
3831
+ throw OPE("JWT Payload must be a top level object", INVALID_RESPONSE, jws);
3832
+ }
3833
+ const now = epochTime() + clockSkew2;
3834
+ if (claims.exp !== undefined) {
3835
+ if (typeof claims.exp !== "number") {
3836
+ throw OPE('unexpected JWT "exp" (expiration time) claim type', INVALID_RESPONSE, { claims });
3837
+ }
3838
+ if (claims.exp <= now - clockTolerance2) {
3839
+ throw OPE('unexpected JWT "exp" (expiration time) claim value, expiration is past current timestamp', JWT_TIMESTAMP_CHECK, { claims, now, tolerance: clockTolerance2, claim: "exp" });
3840
+ }
3841
+ }
3842
+ if (claims.iat !== undefined) {
3843
+ if (typeof claims.iat !== "number") {
3844
+ throw OPE('unexpected JWT "iat" (issued at) claim type', INVALID_RESPONSE, { claims });
3845
+ }
3846
+ }
3847
+ if (claims.iss !== undefined) {
3848
+ if (typeof claims.iss !== "string") {
3849
+ throw OPE('unexpected JWT "iss" (issuer) claim type', INVALID_RESPONSE, { claims });
3850
+ }
3851
+ }
3852
+ if (claims.nbf !== undefined) {
3853
+ if (typeof claims.nbf !== "number") {
3854
+ throw OPE('unexpected JWT "nbf" (not before) claim type', INVALID_RESPONSE, { claims });
3855
+ }
3856
+ if (claims.nbf > now + clockTolerance2) {
3857
+ throw OPE('unexpected JWT "nbf" (not before) claim value', JWT_TIMESTAMP_CHECK, {
3858
+ claims,
3859
+ now,
3860
+ tolerance: clockTolerance2,
3861
+ claim: "nbf"
3862
+ });
3863
+ }
3864
+ }
3865
+ if (claims.aud !== undefined) {
3866
+ if (typeof claims.aud !== "string" && !Array.isArray(claims.aud)) {
3867
+ throw OPE('unexpected JWT "aud" (audience) claim type', INVALID_RESPONSE, { claims });
3868
+ }
3869
+ }
3870
+ return { header, claims, jwt: jws };
3871
+ }
3872
+ function checkSigningAlgorithm(client, issuer, fallback, header) {
3873
+ if (client !== undefined) {
3874
+ if (typeof client === "string" ? header.alg !== client : !client.includes(header.alg)) {
3875
+ throw OPE('unexpected JWT "alg" header parameter', INVALID_RESPONSE, {
3876
+ header,
3877
+ expected: client,
3878
+ reason: "client configuration"
3879
+ });
3880
+ }
3881
+ return;
3882
+ }
3883
+ if (Array.isArray(issuer)) {
3884
+ if (!issuer.includes(header.alg)) {
3885
+ throw OPE('unexpected JWT "alg" header parameter', INVALID_RESPONSE, {
3886
+ header,
3887
+ expected: issuer,
3888
+ reason: "authorization server metadata"
3889
+ });
3890
+ }
3891
+ return;
3892
+ }
3893
+ if (fallback !== undefined) {
3894
+ if (typeof fallback === "string" ? header.alg !== fallback : typeof fallback === "function" ? !fallback(header.alg) : !fallback.includes(header.alg)) {
3895
+ throw OPE('unexpected JWT "alg" header parameter', INVALID_RESPONSE, {
3896
+ header,
3897
+ expected: fallback,
3898
+ reason: "default value"
3899
+ });
3900
+ }
3901
+ return;
3902
+ }
3903
+ throw OPE('missing client or server configuration to verify used JWT "alg" header parameter', undefined, { client, issuer, fallback });
3904
+ }
3905
+ var skipStateCheck = Symbol();
3906
+ var expectNoState = Symbol();
3907
+ function algToSubtle(alg) {
3908
+ switch (alg) {
3909
+ case "PS256":
3910
+ case "PS384":
3911
+ case "PS512":
3912
+ return { name: "RSA-PSS", hash: `SHA-${alg.slice(-3)}` };
3913
+ case "RS256":
3914
+ case "RS384":
3915
+ case "RS512":
3916
+ return { name: "RSASSA-PKCS1-v1_5", hash: `SHA-${alg.slice(-3)}` };
3917
+ case "ES256":
3918
+ case "ES384":
3919
+ return { name: "ECDSA", namedCurve: `P-${alg.slice(-3)}` };
3920
+ case "ES512":
3921
+ return { name: "ECDSA", namedCurve: "P-521" };
3922
+ case "EdDSA":
3923
+ return "Ed25519";
3924
+ case "Ed25519":
3925
+ case "ML-DSA-44":
3926
+ case "ML-DSA-65":
3927
+ case "ML-DSA-87":
3928
+ return alg;
3929
+ default:
3930
+ throw new UnsupportedOperationError("unsupported JWS algorithm", { cause: { alg } });
3931
+ }
3932
+ }
3933
+ async function importJwk(alg, jwk) {
3934
+ const { ext, key_ops, use, ...key } = jwk;
3935
+ return crypto.subtle.importKey("jwk", key, algToSubtle(alg), true, ["verify"]);
3936
+ }
3937
+ function normalizeHtu(htu) {
3938
+ const url = new URL(htu);
3939
+ url.search = "";
3940
+ url.hash = "";
3941
+ return url.href;
3942
+ }
3943
+ async function validateDPoP(request, accessToken, accessTokenClaims, options) {
3944
+ const headerValue = request.headers.get("dpop");
3945
+ if (headerValue === null) {
3946
+ throw OPE("operation indicated DPoP use but the request has no DPoP HTTP Header", INVALID_REQUEST, { headers: request.headers });
3947
+ }
3948
+ if (request.headers.get("authorization")?.toLowerCase().startsWith("dpop ") === false) {
3949
+ throw OPE(`operation indicated DPoP use but the request's Authorization HTTP Header scheme is not DPoP`, INVALID_REQUEST, { headers: request.headers });
3950
+ }
3951
+ if (typeof accessTokenClaims.cnf?.jkt !== "string") {
3952
+ throw OPE("operation indicated DPoP use but the JWT Access Token has no jkt confirmation claim", INVALID_REQUEST, { claims: accessTokenClaims });
3953
+ }
3954
+ const clockSkew2 = getClockSkew(options);
3955
+ const proof = await validateJwt(headerValue, checkSigningAlgorithm.bind(undefined, options?.signingAlgorithms, undefined, supported), clockSkew2, getClockTolerance(options), undefined).then(checkJwtType.bind(undefined, "dpop+jwt")).then(validatePresence.bind(undefined, ["iat", "jti", "ath", "htm", "htu"]));
3956
+ const now = epochTime() + clockSkew2;
3957
+ const diff = Math.abs(now - proof.claims.iat);
3958
+ if (diff > 300) {
3959
+ throw OPE("DPoP Proof iat is not recent enough", JWT_TIMESTAMP_CHECK, {
3960
+ now,
3961
+ claims: proof.claims,
3962
+ claim: "iat"
3963
+ });
3964
+ }
3965
+ if (proof.claims.htm !== request.method) {
3966
+ throw OPE("DPoP Proof htm mismatch", JWT_CLAIM_COMPARISON, {
3967
+ expected: request.method,
3968
+ claims: proof.claims,
3969
+ claim: "htm"
3970
+ });
3971
+ }
3972
+ if (typeof proof.claims.htu !== "string" || normalizeHtu(proof.claims.htu) !== normalizeHtu(request.url)) {
3973
+ throw OPE("DPoP Proof htu mismatch", JWT_CLAIM_COMPARISON, {
3974
+ expected: normalizeHtu(request.url),
3975
+ claims: proof.claims,
3976
+ claim: "htu"
3977
+ });
3978
+ }
3979
+ {
3980
+ const expected = b64u(await crypto.subtle.digest("SHA-256", buf(accessToken)));
3981
+ if (proof.claims.ath !== expected) {
3982
+ throw OPE("DPoP Proof ath mismatch", JWT_CLAIM_COMPARISON, {
3983
+ expected,
3984
+ claims: proof.claims,
3985
+ claim: "ath"
3986
+ });
3987
+ }
3988
+ }
3989
+ {
3990
+ const expected = await calculateJwkThumbprint(proof.header.jwk);
3991
+ if (accessTokenClaims.cnf.jkt !== expected) {
3992
+ throw OPE("JWT Access Token confirmation mismatch", JWT_CLAIM_COMPARISON, {
3993
+ expected,
3994
+ claims: accessTokenClaims,
3995
+ claim: "cnf.jkt"
3996
+ });
3997
+ }
3998
+ }
3999
+ const { 0: protectedHeader, 1: payload, 2: encodedSignature } = headerValue.split(".");
4000
+ const signature = b64u(encodedSignature);
4001
+ const { jwk, alg } = proof.header;
4002
+ if (!jwk) {
4003
+ throw OPE("DPoP Proof is missing the jwk header parameter", INVALID_REQUEST, {
4004
+ header: proof.header
4005
+ });
4006
+ }
4007
+ const key = await importJwk(alg, jwk);
4008
+ if (key.type !== "public") {
4009
+ throw OPE("DPoP Proof jwk header parameter must contain a public key", INVALID_REQUEST, {
4010
+ header: proof.header
4011
+ });
4012
+ }
4013
+ await validateJwsSignature(protectedHeader, payload, key, signature);
4014
+ }
4015
+ async function validateJwtAccessToken(as, request, expectedAudience, options) {
4016
+ assertAs(as);
4017
+ if (!looseInstanceOf(request, Request)) {
4018
+ throw CodedTypeError('"request" must be an instance of Request', ERR_INVALID_ARG_TYPE);
4019
+ }
4020
+ assertString(expectedAudience, '"expectedAudience"');
4021
+ const authorization = request.headers.get("authorization");
4022
+ if (authorization === null) {
4023
+ throw OPE('"request" is missing an Authorization HTTP Header', INVALID_REQUEST, {
4024
+ headers: request.headers
4025
+ });
4026
+ }
4027
+ let { 0: scheme, 1: accessToken, length } = authorization.split(" ");
4028
+ scheme = scheme.toLowerCase();
4029
+ switch (scheme) {
4030
+ case "dpop":
4031
+ case "bearer":
4032
+ break;
4033
+ default:
4034
+ throw new UnsupportedOperationError("unsupported Authorization HTTP Header scheme", {
4035
+ cause: { headers: request.headers }
4036
+ });
4037
+ }
4038
+ if (length !== 2) {
4039
+ throw OPE("invalid Authorization HTTP Header format", INVALID_REQUEST, {
4040
+ headers: request.headers
4041
+ });
4042
+ }
4043
+ const requiredClaims = [
4044
+ "iss",
4045
+ "exp",
4046
+ "aud",
4047
+ "sub",
4048
+ "iat",
4049
+ "jti",
4050
+ "client_id"
4051
+ ];
4052
+ if (options?.requireDPoP || scheme === "dpop" || request.headers.has("dpop")) {
4053
+ requiredClaims.push("cnf");
4054
+ }
4055
+ const { claims, header } = await validateJwt(accessToken, checkSigningAlgorithm.bind(undefined, options?.signingAlgorithms, undefined, supported), getClockSkew(options), getClockTolerance(options), undefined).then(checkJwtType.bind(undefined, "at+jwt")).then(validatePresence.bind(undefined, requiredClaims)).then(validateIssuer.bind(undefined, as)).then(validateAudience.bind(undefined, expectedAudience)).catch(reassignRSCode);
4056
+ for (const claim of ["client_id", "jti", "sub"]) {
4057
+ if (typeof claims[claim] !== "string") {
4058
+ throw OPE(`unexpected JWT "${claim}" claim type`, INVALID_REQUEST, { claims });
4059
+ }
4060
+ }
4061
+ if ("cnf" in claims) {
4062
+ if (!isJsonObject(claims.cnf)) {
4063
+ throw OPE('unexpected JWT "cnf" (confirmation) claim value', INVALID_REQUEST, { claims });
4064
+ }
4065
+ const { 0: cnf, length: length2 } = Object.keys(claims.cnf);
4066
+ if (length2) {
4067
+ if (length2 !== 1) {
4068
+ throw new UnsupportedOperationError("multiple confirmation claims are not supported", {
4069
+ cause: { claims }
4070
+ });
4071
+ }
4072
+ if (cnf !== "jkt") {
4073
+ throw new UnsupportedOperationError("unsupported JWT Confirmation method", {
4074
+ cause: { claims }
4075
+ });
4076
+ }
4077
+ }
4078
+ }
4079
+ const { 0: protectedHeader, 1: payload, 2: encodedSignature } = accessToken.split(".");
4080
+ const signature = b64u(encodedSignature);
4081
+ const key = await getPublicSigKeyFromIssuerJwksUri(as, options, header);
4082
+ await validateJwsSignature(protectedHeader, payload, key, signature);
4083
+ if (options?.requireDPoP || scheme === "dpop" || claims.cnf?.jkt !== undefined || request.headers.has("dpop")) {
4084
+ await validateDPoP(request, accessToken, claims, options).catch(reassignRSCode);
4085
+ }
4086
+ return claims;
4087
+ }
4088
+ function reassignRSCode(err) {
4089
+ if (err instanceof OperationProcessingError && err?.code === INVALID_REQUEST) {
4090
+ err.code = INVALID_RESPONSE;
4091
+ }
4092
+ throw err;
4093
+ }
4094
+ async function getResponseJsonBody(response, check = assertApplicationJson) {
4095
+ let json;
4096
+ try {
4097
+ json = await response.json();
4098
+ } catch (cause) {
4099
+ check(response);
4100
+ throw OPE('failed to parse "response" body as JSON', PARSE_ERROR, cause);
4101
+ }
4102
+ if (!isJsonObject(json)) {
4103
+ throw OPE('"response" body must be a top level object', INVALID_RESPONSE, { body: json });
4104
+ }
4105
+ return json;
4106
+ }
4107
+ var _nodiscoverycheck = Symbol();
4108
+ var _expectedIssuer = Symbol();
4109
+
4110
+ // src/http/jwt.ts
4111
+ function jwtAuthenticate(options) {
4112
+ const principalClaim = options.principalClaim ?? "sub";
4113
+ const domain = options.domain ?? "jwt";
4114
+ const audience = options.audience;
4115
+ let asPromise = null;
4116
+ async function getAuthorizationServer() {
4117
+ if (options.jwksUri) {
4118
+ return {
4119
+ issuer: options.issuer,
4120
+ jwks_uri: options.jwksUri
4121
+ };
4122
+ }
4123
+ const issuerUrl = new URL(options.issuer);
4124
+ const response = await discoveryRequest(issuerUrl);
4125
+ return processDiscoveryResponse(issuerUrl, response);
4126
+ }
4127
+ return async function authenticate(request) {
4128
+ if (!asPromise) {
4129
+ asPromise = getAuthorizationServer();
4130
+ }
4131
+ let as;
4132
+ try {
4133
+ as = await asPromise;
4134
+ } catch (error) {
4135
+ asPromise = null;
4136
+ throw error;
4137
+ }
4138
+ const audiences = Array.isArray(audience) ? audience : [audience];
4139
+ let claims;
4140
+ let lastError;
4141
+ for (const aud of audiences) {
4142
+ try {
4143
+ claims = await validateJwtAccessToken(as, request, aud);
4144
+ break;
4145
+ } catch (error) {
4146
+ lastError = error;
4147
+ }
4148
+ }
4149
+ if (!claims) {
4150
+ throw lastError;
4151
+ }
4152
+ const principal = claims[principalClaim] ?? null;
4153
+ return new AuthContext(domain, true, principal, claims);
4154
+ };
4155
+ }
4156
+ // src/http/mtls.ts
4157
+ import { createHash, X509Certificate } from "node:crypto";
4158
+ function splitRespectingQuotes(text, delimiter) {
4159
+ const parts = [];
4160
+ let current = [];
4161
+ let inQuotes = false;
4162
+ let i = 0;
4163
+ while (i < text.length) {
4164
+ const ch = text[i];
4165
+ if (ch === '"') {
4166
+ inQuotes = !inQuotes;
4167
+ current.push(ch);
4168
+ } else if (ch === "\\" && inQuotes && i + 1 < text.length) {
4169
+ current.push(ch);
4170
+ current.push(text[i + 1]);
4171
+ i++;
4172
+ } else if (ch === delimiter && !inQuotes) {
4173
+ parts.push(current.join(""));
4174
+ current = [];
4175
+ } else {
4176
+ current.push(ch);
4177
+ }
4178
+ i++;
4179
+ }
4180
+ parts.push(current.join(""));
4181
+ return parts;
4182
+ }
4183
+ function unescapeQuoted(text) {
4184
+ return text.replace(/\\(.)/g, "$1");
4185
+ }
4186
+ function extractCn(subject) {
4187
+ for (const part of subject.split(/(?<!\\),/)) {
4188
+ const trimmed = part.trim();
4189
+ if (trimmed.toUpperCase().startsWith("CN=")) {
4190
+ return trimmed.slice(3);
4191
+ }
4192
+ }
4193
+ return "";
4194
+ }
4195
+ function parseXfcc(headerValue) {
4196
+ const elements = [];
4197
+ for (const rawElement of splitRespectingQuotes(headerValue, ",")) {
4198
+ const trimmed = rawElement.trim();
4199
+ if (!trimmed)
4200
+ continue;
4201
+ const pairs = splitRespectingQuotes(trimmed, ";");
4202
+ const fields = {};
4203
+ for (const pair of pairs) {
4204
+ const p = pair.trim();
4205
+ if (!p)
4206
+ continue;
4207
+ const eqIdx = p.indexOf("=");
4208
+ if (eqIdx < 0)
4209
+ continue;
4210
+ const key = p.slice(0, eqIdx).trim().toLowerCase();
4211
+ let value = p.slice(eqIdx + 1).trim();
4212
+ if (value.length >= 2 && value[0] === '"' && value[value.length - 1] === '"') {
4213
+ value = unescapeQuoted(value.slice(1, -1));
4214
+ }
4215
+ if (key === "cert" || key === "uri" || key === "by") {
4216
+ value = decodeURIComponent(value);
4217
+ }
4218
+ if (key === "dns") {
4219
+ const existing = fields.dns;
4220
+ if (Array.isArray(existing)) {
4221
+ existing.push(value);
4222
+ } else {
4223
+ fields.dns = [value];
4224
+ }
4225
+ } else {
4226
+ fields[key] = value;
4227
+ }
4228
+ }
4229
+ const dns = Array.isArray(fields.dns) ? fields.dns : [];
4230
+ elements.push({
4231
+ hash: typeof fields.hash === "string" ? fields.hash : null,
4232
+ cert: typeof fields.cert === "string" ? fields.cert : null,
4233
+ subject: typeof fields.subject === "string" ? fields.subject : null,
4234
+ uri: typeof fields.uri === "string" ? fields.uri : null,
4235
+ dns,
4236
+ by: typeof fields.by === "string" ? fields.by : null
4237
+ });
4238
+ }
4239
+ return elements;
4240
+ }
4241
+ function mtlsAuthenticateXfcc(options) {
4242
+ const validate = options?.validate;
4243
+ const domain = options?.domain ?? "mtls";
4244
+ const selectElement = options?.selectElement ?? "first";
4245
+ return async function authenticate(request) {
4246
+ const headerValue = request.headers.get("x-forwarded-client-cert");
4247
+ if (!headerValue) {
4248
+ throw new Error("Missing x-forwarded-client-cert header");
4249
+ }
4250
+ const elements = parseXfcc(headerValue);
4251
+ if (elements.length === 0) {
4252
+ throw new Error("Empty x-forwarded-client-cert header");
4253
+ }
4254
+ const element = selectElement === "first" ? elements[0] : elements[elements.length - 1];
4255
+ if (validate) {
4256
+ return validate(element);
4257
+ }
4258
+ const principal = element.subject ? extractCn(element.subject) : "";
4259
+ const claims = {};
4260
+ if (element.hash)
4261
+ claims.hash = element.hash;
4262
+ if (element.subject)
4263
+ claims.subject = element.subject;
4264
+ if (element.uri)
4265
+ claims.uri = element.uri;
4266
+ if (element.dns.length > 0)
4267
+ claims.dns = [...element.dns];
4268
+ if (element.by)
4269
+ claims.by = element.by;
4270
+ return new AuthContext(domain, true, principal, claims);
4271
+ };
4272
+ }
4273
+ function parseCertFromHeader(request, header) {
4274
+ const raw = request.headers.get(header);
4275
+ if (!raw) {
4276
+ throw new Error(`Missing ${header} header`);
4277
+ }
4278
+ const pemStr = decodeURIComponent(raw);
4279
+ if (!pemStr.startsWith("-----BEGIN CERTIFICATE-----")) {
4280
+ throw new Error("Header value is not a PEM certificate");
4281
+ }
4282
+ try {
4283
+ return new X509Certificate(pemStr);
4284
+ } catch (exc) {
4285
+ throw new Error(`Failed to parse PEM certificate: ${exc}`);
4286
+ }
4287
+ }
4288
+ function checkCertExpiry(cert) {
4289
+ const now = new Date;
4290
+ const notBefore = new Date(cert.validFrom);
4291
+ const notAfter = new Date(cert.validTo);
4292
+ if (now < notBefore) {
4293
+ throw new Error("Certificate is not yet valid");
4294
+ }
4295
+ if (now > notAfter) {
4296
+ throw new Error("Certificate has expired");
4297
+ }
4298
+ }
4299
+ function mtlsAuthenticate(options) {
4300
+ const { validate, header = "X-SSL-Client-Cert", checkExpiry = false } = options;
4301
+ return async function authenticate(request) {
4302
+ const cert = parseCertFromHeader(request, header);
4303
+ if (checkExpiry) {
4304
+ checkCertExpiry(cert);
4305
+ }
4306
+ return validate(cert);
4307
+ };
4308
+ }
4309
+ var SUPPORTED_ALGORITHMS = new Set(["sha256", "sha1", "sha384", "sha512"]);
4310
+ function mtlsAuthenticateFingerprint(options) {
4311
+ const { fingerprints, header, algorithm = "sha256", checkExpiry } = options;
4312
+ if (!SUPPORTED_ALGORITHMS.has(algorithm)) {
4313
+ throw new Error(`Unsupported hash algorithm: ${algorithm}`);
4314
+ }
4315
+ const entries = fingerprints instanceof Map ? fingerprints : new Map(Object.entries(fingerprints));
4316
+ function validate(cert) {
4317
+ const fp = createHash(algorithm).update(cert.raw).digest("hex");
4318
+ const ctx = entries.get(fp);
4319
+ if (!ctx) {
4320
+ throw new Error(`Unknown certificate fingerprint: ${fp}`);
4321
+ }
4322
+ return ctx;
4323
+ }
4324
+ return mtlsAuthenticate({ validate, header, checkExpiry });
4325
+ }
4326
+ function mtlsAuthenticateSubject(options) {
4327
+ const { header, domain = "mtls", allowedSubjects = null, checkExpiry } = options ?? {};
4328
+ function validate(cert) {
4329
+ const subjectParts = cert.subject.split(`
4330
+ `).map((s) => s.trim()).filter(Boolean);
4331
+ const subjectDn = subjectParts.join(", ");
4332
+ let cn = "";
4333
+ for (const part of subjectParts) {
4334
+ if (part.toUpperCase().startsWith("CN=")) {
4335
+ cn = part.slice(3);
4336
+ break;
4337
+ }
4338
+ }
4339
+ if (allowedSubjects !== null && !allowedSubjects.has(cn)) {
4340
+ throw new Error(`Subject CN '${cn}' not in allowed subjects`);
4341
+ }
4342
+ const serialHex = BigInt(`0x${cert.serialNumber}`).toString(16);
4343
+ const notValidAfter = new Date(cert.validTo).toISOString();
4344
+ return new AuthContext(domain, true, cn, {
4345
+ subject_dn: subjectDn,
4346
+ serial: serialHex,
4347
+ not_valid_after: notValidAfter
4348
+ });
4349
+ }
4350
+ return mtlsAuthenticate({ validate, header, checkExpiry });
4351
+ }
4352
+ // src/protocol.ts
4353
+ import { Schema as Schema7 } from "@query-farm/apache-arrow";
4354
+
4355
+ // src/schema.ts
4356
+ import {
4357
+ Binary as Binary3,
4358
+ Bool as Bool3,
4359
+ DataType as DataType4,
4360
+ Field as Field4,
4361
+ Float32,
4362
+ Float64 as Float642,
4363
+ Int16,
4364
+ Int32,
4365
+ Int64 as Int642,
4366
+ Schema as Schema6,
4367
+ Utf8 as Utf83
4368
+ } from "@query-farm/apache-arrow";
4369
+ var str = new Utf83;
4370
+ var bytes = new Binary3;
4371
+ var int = new Int642;
4372
+ var int32 = new Int32;
4373
+ var float = new Float642;
4374
+ var float32 = new Float32;
4375
+ var bool = new Bool3;
4376
+ function toSchema(spec) {
4377
+ if (spec instanceof Schema6)
4378
+ return spec;
4379
+ const fields = [];
4380
+ for (const [name, value] of Object.entries(spec)) {
4381
+ if (value instanceof Field4) {
4382
+ fields.push(value);
4383
+ } else if (value instanceof DataType4) {
4384
+ fields.push(new Field4(name, value, false));
4385
+ } else {
4386
+ throw new TypeError(`Invalid schema value for "${name}": expected DataType or Field, got ${typeof value}`);
4387
+ }
4388
+ }
4389
+ return new Schema6(fields);
4390
+ }
4391
+ var TYPE_MAP = [
4392
+ [Utf83, "str"],
4393
+ [Binary3, "bytes"],
4394
+ [Bool3, "bool"],
4395
+ [Float642, "float"],
4396
+ [Float32, "float"],
4397
+ [Int642, "int"],
4398
+ [Int32, "int"],
4399
+ [Int16, "int"]
4400
+ ];
4401
+ function inferParamTypes(spec) {
4402
+ const schema = toSchema(spec);
4403
+ if (schema.fields.length === 0)
4404
+ return;
4405
+ const result = {};
4406
+ for (const field of schema.fields) {
4407
+ let mapped;
4408
+ for (const [ctor, name] of TYPE_MAP) {
4409
+ if (field.type instanceof ctor) {
4410
+ mapped = name;
4411
+ break;
4412
+ }
4413
+ }
4414
+ if (!mapped)
4415
+ return;
4416
+ result[field.name] = mapped;
4417
+ }
4418
+ return result;
4419
+ }
4420
+
4421
+ // src/protocol.ts
4422
+ var EMPTY_SCHEMA3 = new Schema7([]);
4423
+
4424
+ class Protocol {
4425
+ name;
4426
+ _methods = new Map;
4427
+ constructor(name) {
4428
+ this.name = name;
4429
+ }
4430
+ unary(name, config) {
4431
+ const params = toSchema(config.params);
4432
+ this._methods.set(name, {
4433
+ name,
4434
+ type: "unary" /* UNARY */,
4435
+ paramsSchema: params,
2234
4436
  resultSchema: toSchema(config.result),
2235
4437
  handler: config.handler,
2236
4438
  doc: config.doc,
@@ -2287,7 +4489,7 @@ import { Schema as Schema9 } from "@query-farm/apache-arrow";
2287
4489
  // src/dispatch/stream.ts
2288
4490
  import { Schema as Schema8 } from "@query-farm/apache-arrow";
2289
4491
  var EMPTY_SCHEMA4 = new Schema8([]);
2290
- async function dispatchStream(method, params, writer, reader, serverId, requestId) {
4492
+ async function dispatchStream(method, params, writer, reader, serverId, requestId, externalConfig) {
2291
4493
  const isProducer = !!method.producerFn;
2292
4494
  let state;
2293
4495
  try {
@@ -2341,8 +4543,11 @@ async function dispatchStream(method, params, writer, reader, serverId, requestI
2341
4543
  if (expectedInputSchema && !isProducer && inputBatch.schema !== expectedInputSchema) {
2342
4544
  try {
2343
4545
  inputBatch = conformBatchToSchema(inputBatch, expectedInputSchema);
2344
- } catch {
2345
- throw new TypeError(`Input schema mismatch: expected ${expectedInputSchema}, got ${inputBatch.schema}`);
4546
+ } catch (e) {
4547
+ if (e instanceof TypeError) {
4548
+ throw e;
4549
+ }
4550
+ console.debug?.(`Schema conformance skipped: ${e instanceof Error ? e.message : e}`);
2346
4551
  }
2347
4552
  }
2348
4553
  const out = new OutputCollector(outputSchema, effectiveProducer, serverId, requestId);
@@ -2352,7 +4557,11 @@ async function dispatchStream(method, params, writer, reader, serverId, requestI
2352
4557
  await method.exchangeFn(state, inputBatch, out);
2353
4558
  }
2354
4559
  for (const emitted of out.batches) {
2355
- stream.write(emitted.batch);
4560
+ let batch = emitted.batch;
4561
+ if (externalConfig) {
4562
+ batch = await maybeExternalizeBatch(batch, externalConfig);
4563
+ }
4564
+ stream.write(batch);
2356
4565
  }
2357
4566
  if (out.finished) {
2358
4567
  break;
@@ -2368,12 +4577,15 @@ async function dispatchStream(method, params, writer, reader, serverId, requestI
2368
4577
  }
2369
4578
 
2370
4579
  // src/dispatch/unary.ts
2371
- async function dispatchUnary(method, params, writer, serverId, requestId) {
4580
+ async function dispatchUnary(method, params, writer, serverId, requestId, externalConfig) {
2372
4581
  const schema = method.resultSchema;
2373
4582
  const out = new OutputCollector(schema, true, serverId, requestId);
2374
4583
  try {
2375
4584
  const result = await method.handler(params, out);
2376
- const resultBatch = buildResultBatch(schema, result, serverId, requestId);
4585
+ let resultBatch = buildResultBatch(schema, result, serverId, requestId);
4586
+ if (externalConfig) {
4587
+ resultBatch = await maybeExternalizeBatch(resultBatch, externalConfig);
4588
+ }
2377
4589
  const batches = [...out.batches.map((b) => b.batch), resultBatch];
2378
4590
  writer.writeStream(schema, batches);
2379
4591
  } catch (error) {
@@ -2384,7 +4596,7 @@ async function dispatchUnary(method, params, writer, serverId, requestId) {
2384
4596
 
2385
4597
  // src/wire/writer.ts
2386
4598
  import { writeSync } from "node:fs";
2387
- import { RecordBatchStreamWriter as RecordBatchStreamWriter4 } from "@query-farm/apache-arrow";
4599
+ import { RecordBatchStreamWriter as RecordBatchStreamWriter5 } from "@query-farm/apache-arrow";
2388
4600
  var STDOUT_FD = 1;
2389
4601
  function writeAll(fd, data) {
2390
4602
  let offset = 0;
@@ -2410,7 +4622,7 @@ class IpcStreamWriter {
2410
4622
  this.fd = fd;
2411
4623
  }
2412
4624
  writeStream(schema, batches) {
2413
- const writer = new RecordBatchStreamWriter4;
4625
+ const writer = new RecordBatchStreamWriter5;
2414
4626
  writer.reset(undefined, schema);
2415
4627
  for (const batch of batches) {
2416
4628
  writer._writeRecordBatch(batch);
@@ -2430,7 +4642,7 @@ class IncrementalStream {
2430
4642
  closed = false;
2431
4643
  constructor(fd, schema) {
2432
4644
  this.fd = fd;
2433
- this.writer = new RecordBatchStreamWriter4;
4645
+ this.writer = new RecordBatchStreamWriter5;
2434
4646
  this.writer.reset(undefined, schema);
2435
4647
  this.drain();
2436
4648
  }
@@ -2464,10 +4676,14 @@ class VgiRpcServer {
2464
4676
  enableDescribe;
2465
4677
  serverId;
2466
4678
  describeBatch = null;
4679
+ dispatchHook = null;
4680
+ externalConfig;
2467
4681
  constructor(protocol, options) {
2468
4682
  this.protocol = protocol;
2469
4683
  this.enableDescribe = options?.enableDescribe ?? true;
2470
4684
  this.serverId = options?.serverId ?? crypto.randomUUID().replace(/-/g, "").slice(0, 12);
4685
+ this.dispatchHook = options?.dispatchHook ?? null;
4686
+ this.externalConfig = options?.externalLocation;
2471
4687
  if (this.enableDescribe) {
2472
4688
  const { batch } = buildDescribeBatch(protocol.name, protocol.getMethods(), this.serverId);
2473
4689
  this.describeBatch = batch;
@@ -2537,10 +4753,29 @@ class VgiRpcServer {
2537
4753
  writer.writeStream(EMPTY_SCHEMA5, [errBatch]);
2538
4754
  return;
2539
4755
  }
2540
- if (method.type === "unary" /* UNARY */) {
2541
- await dispatchUnary(method, params, writer, this.serverId, requestId);
2542
- } else {
2543
- await dispatchStream(method, params, writer, reader, this.serverId, requestId);
4756
+ const methodType = method.type === "unary" /* UNARY */ ? "unary" : "stream";
4757
+ const info = { method: methodName, methodType, serverId: this.serverId, requestId };
4758
+ const stats = {
4759
+ inputBatches: 0,
4760
+ outputBatches: 0,
4761
+ inputRows: 0,
4762
+ outputRows: 0,
4763
+ inputBytes: 0,
4764
+ outputBytes: 0
4765
+ };
4766
+ const token = this.dispatchHook?.onDispatchStart(info);
4767
+ let dispatchError;
4768
+ try {
4769
+ if (method.type === "unary" /* UNARY */) {
4770
+ await dispatchUnary(method, params, writer, this.serverId, requestId, this.externalConfig);
4771
+ } else {
4772
+ await dispatchStream(method, params, writer, reader, this.serverId, requestId, this.externalConfig);
4773
+ }
4774
+ } catch (e) {
4775
+ dispatchError = e instanceof Error ? e : new Error(String(e));
4776
+ throw e;
4777
+ } finally {
4778
+ this.dispatchHook?.onDispatchEnd(token, info, stats, dispatchError);
2544
4779
  }
2545
4780
  }
2546
4781
  }
@@ -2549,19 +4784,42 @@ export {
2549
4784
  toSchema,
2550
4785
  subprocessConnect,
2551
4786
  str,
4787
+ resolveExternalLocation,
2552
4788
  pipeConnect,
4789
+ parseXfcc,
4790
+ parseUseIdTokenAsBearer,
4791
+ parseResourceMetadataUrl,
4792
+ parseDeviceCodeClientSecret,
4793
+ parseDeviceCodeClientId,
2553
4794
  parseDescribeResponse,
4795
+ parseClientSecret,
4796
+ parseClientId,
4797
+ oauthResourceMetadataToJson,
4798
+ mtlsAuthenticateXfcc,
4799
+ mtlsAuthenticateSubject,
4800
+ mtlsAuthenticateFingerprint,
4801
+ mtlsAuthenticate,
4802
+ maybeExternalizeBatch,
4803
+ makeExternalLocationBatch,
4804
+ jwtAuthenticate,
2554
4805
  jsonStateSerializer,
4806
+ isExternalLocationBatch,
2555
4807
  int32,
2556
4808
  int,
2557
4809
  inferParamTypes,
4810
+ httpsOnlyValidator,
4811
+ httpOAuthMetadata,
2558
4812
  httpIntrospect,
2559
4813
  httpConnect,
2560
4814
  float32,
2561
4815
  float,
4816
+ fetchOAuthMetadata,
2562
4817
  createHttpHandler,
4818
+ chainAuthenticate,
2563
4819
  bytes,
2564
4820
  bool,
4821
+ bearerAuthenticateStatic,
4822
+ bearerAuthenticate,
2565
4823
  VgiRpcServer,
2566
4824
  VersionError,
2567
4825
  STATE_KEY,
@@ -2583,7 +4841,8 @@ export {
2583
4841
  DESCRIBE_VERSION_KEY,
2584
4842
  DESCRIBE_VERSION,
2585
4843
  DESCRIBE_METHOD_NAME,
4844
+ AuthContext,
2586
4845
  ARROW_CONTENT_TYPE
2587
4846
  };
2588
4847
 
2589
- //# debugId=A10C22D56B2874CF64756E2164756E21
4848
+ //# debugId=37CD76108224132A64756E2164756E21