@oddington/sdk 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -21668,7 +21668,7 @@ function managedConnections(capability) {
21668
21668
  }
21669
21669
 
21670
21670
  // src/connections.ts
21671
- var connections = createConnections(async (name, operation, input) => {
21671
+ var connections = /* @__PURE__ */ createConnections(async (name, operation, input) => {
21672
21672
  try {
21673
21673
  const managed = process.env[MANAGED_CONNECTION_CAPABILITY_ENV];
21674
21674
  if (managed) {
@@ -22000,7 +22000,15 @@ function oddingtonOrigin(environment = process.env) {
22000
22000
  }
22001
22001
 
22002
22002
  // src/routine-local-transport.ts
22003
- function privateRoutineRequest(environment = typeof process === "undefined" ? {} : process.env) {
22003
+ var nodeRequest;
22004
+ var nodeReadable;
22005
+ if (typeof __ODDINGTON_BROWSER__ === "undefined" || !__ODDINGTON_BROWSER__) {
22006
+ ({ request: nodeRequest } = await import("node:http"));
22007
+ ({ Readable: nodeReadable } = await import("node:stream"));
22008
+ }
22009
+ function privateRoutineRequest(environment = typeof __ODDINGTON_BROWSER__ !== "undefined" && __ODDINGTON_BROWSER__ ? {} : typeof process === "undefined" ? {} : process.env) {
22010
+ if (typeof __ODDINGTON_BROWSER__ !== "undefined" && __ODDINGTON_BROWSER__)
22011
+ return;
22004
22012
  const prepared = ["ODDINGTON_ROUTINE_LOCAL", "ODDINGTON_ROUTINE_LOCAL_SOCKET", "ODDINGTON_ROUTINE_LOCAL_TOKEN"].some((key) => environment[key] !== undefined);
22005
22013
  const managed = ["ODDINGTON_FILES_SOCKET", "ODDINGTON_FILES_TOKEN"].some((key) => environment[key] !== undefined);
22006
22014
  if (!prepared && !managed)
@@ -22013,83 +22021,302 @@ function privateRoutineRequest(environment = typeof process === "undefined" ? {}
22013
22021
  ODDINGTON_ROUTINE_LOCAL_TOKEN: token
22014
22022
  });
22015
22023
  const isCurrent = () => prepared ? environment.ODDINGTON_ROUTINE_LOCAL === "1" && environment.ODDINGTON_ROUTINE_LOCAL_SOCKET === socket && environment.ODDINGTON_ROUTINE_LOCAL_TOKEN === token : environment.ODDINGTON_FILES_SOCKET === socket && environment.ODDINGTON_FILES_TOKEN === token;
22016
- return { request, isCurrent, identity: `private:${socket}` };
22017
- }
22018
- function localRoutineRequest(environment = typeof process === "undefined" ? {} : process.env) {
22019
- const {
22020
- ODDINGTON_ROUTINE_LOCAL: mode,
22021
- ODDINGTON_ROUTINE_LOCAL_SOCKET: socket,
22022
- ODDINGTON_ROUTINE_LOCAL_TOKEN: token
22023
- } = environment;
22024
- if (mode === undefined && socket === undefined && token === undefined)
22025
- return;
22026
- if (mode !== "1" || !socket || !token || !/^[0-9a-f]{64}$/.test(token))
22027
- throw new Error("Preparation transport unavailable; nothing can publish");
22028
- const tcp = /^tcp:\/\/127\.0\.0\.1:([1-9][0-9]{0,4})$/.exec(socket);
22029
- const endpoint = tcp && tcp[0] === socket && Number(tcp[1]) <= 65535 ? { hostname: "127.0.0.1", port: Number(tcp[1]) } : socket.startsWith("/") && !socket.startsWith("//") && !socket.includes("\x00") ? { socketPath: socket } : undefined;
22030
- if (!endpoint)
22031
- throw new Error("Preparation transport unavailable; nothing can publish");
22032
- return async (operation, input) => {
22033
- const { request } = await import("node:http");
22034
- return await new Promise((resolve, reject) => {
22035
- const call = request({
22024
+ if (!nodeRequest || !nodeReadable)
22025
+ throw new Error("Files transport unavailable");
22026
+ const httpRequest = nodeRequest, Readable = nodeReadable;
22027
+ const files = async (suffix, init, signal) => {
22028
+ if (!isCurrent())
22029
+ throw new FileOperationError("unsupported_context", 400);
22030
+ const tcp = /^tcp:\/\/127\.0\.0\.1:([1-9][0-9]{0,4})$/.exec(socket);
22031
+ const endpoint = tcp ? { hostname: "127.0.0.1", port: Number(tcp[1]) } : { socketPath: socket };
22032
+ const id2 = crypto.randomUUID();
22033
+ if (init.body instanceof ReadableStream) {
22034
+ await new Promise((resolve, reject) => {
22035
+ const admission = httpRequest({
22036
+ ...endpoint,
22037
+ path: "/files/admit",
22038
+ method: "POST",
22039
+ timeout: 30000,
22040
+ signal,
22041
+ agent: false,
22042
+ headers: {
22043
+ ...Object.fromEntries(new Headers(init.headers)),
22044
+ authorization: `Bearer ${token}`,
22045
+ "x-oddington-files-suffix": encodeURIComponent(suffix),
22046
+ "x-oddington-files-method": init.method ?? "GET"
22047
+ }
22048
+ }, (response) => {
22049
+ const bytes = Buffer.alloc(16384);
22050
+ let size = 0;
22051
+ response.on("data", (chunk) => {
22052
+ size += chunk.length;
22053
+ if (size > 16384)
22054
+ response.destroy(new Error("Files admission result exceeds supported size"));
22055
+ else
22056
+ bytes.set(chunk, size - chunk.length);
22057
+ });
22058
+ response.once("error", reject);
22059
+ response.once("end", () => {
22060
+ if (response.statusCode === 204) {
22061
+ resolve();
22062
+ return;
22063
+ }
22064
+ try {
22065
+ const value = JSON.parse(bytes.subarray(0, size).toString());
22066
+ const code = value?.error?.code;
22067
+ const status = response.statusCode ?? 503;
22068
+ if (["invalid_input", "request_too_large", "unsupported_context", "access_denied", "capacity", "unavailable"].includes(code))
22069
+ reject(new FileOperationError(code, status, code === "capacity" || code === "unavailable"));
22070
+ else
22071
+ reject(new FileOperationError("unavailable", 503, true));
22072
+ } catch {
22073
+ reject(new FileOperationError("unavailable", 503, true));
22074
+ }
22075
+ });
22076
+ });
22077
+ admission.once("error", () => reject(new FileOperationError("unavailable", 503, true)));
22078
+ admission.once("timeout", () => admission.destroy(new Error("Files admission stalled")));
22079
+ admission.end();
22080
+ });
22081
+ if (!isCurrent())
22082
+ throw new FileOperationError("unsupported_context", 400);
22083
+ }
22084
+ let cancellation;
22085
+ const cancelCall = () => cancellation ??= new Promise((resolve, reject) => {
22086
+ const cancel = httpRequest({
22036
22087
  ...endpoint,
22037
- path: `/${operation}`,
22088
+ path: "/files/cancel",
22038
22089
  method: "POST",
22039
- headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
22040
- timeout: 120000
22090
+ timeout: 30000,
22091
+ headers: { authorization: `Bearer ${token}`, "x-oddington-files-call": id2 }
22041
22092
  }, (response) => {
22042
- const bytes = Buffer.alloc(33554432);
22043
- let size = 0;
22044
- response.on("data", (chunk) => {
22045
- size += chunk.length;
22046
- if (size > 33554432) {
22093
+ response.resume();
22094
+ response.once("end", resolve);
22095
+ response.once("error", reject);
22096
+ });
22097
+ cancel.once("error", reject);
22098
+ cancel.once("timeout", () => cancel.destroy(new Error("Files cancellation stalled")));
22099
+ cancel.end();
22100
+ });
22101
+ let pumping = Promise.resolve();
22102
+ let sourceReader;
22103
+ const result = new Promise((resolve, reject) => {
22104
+ const call = httpRequest({
22105
+ ...endpoint,
22106
+ path: "/files/stream",
22107
+ method: "POST",
22108
+ headers: {
22109
+ ...Object.fromEntries(new Headers(init.headers)),
22110
+ authorization: `Bearer ${token}`,
22111
+ "x-oddington-files-call": id2,
22112
+ "x-oddington-files-suffix": encodeURIComponent(suffix),
22113
+ "x-oddington-files-method": init.method ?? "GET"
22114
+ },
22115
+ signal,
22116
+ timeout: 30000,
22117
+ agent: false
22118
+ }, async (response) => {
22119
+ try {
22120
+ call.setTimeout(0);
22121
+ if ((response.statusCode ?? 503) >= 400) {
22122
+ await sourceReader?.cancel().catch(() => {});
22123
+ call.end();
22124
+ }
22125
+ await pumping;
22126
+ if (!isCurrent()) {
22047
22127
  response.destroy();
22048
- reject(new Error("Local result exceeds supported size"));
22049
- } else
22050
- bytes.set(chunk, size - chunk.length);
22051
- });
22052
- response.on("error", reject);
22053
- response.on("end", () => {
22054
- try {
22055
- const result = JSON.parse(bytes.subarray(0, size).toString());
22056
- if (response.statusCode !== 200) {
22057
- if (operation === "files/request") {
22058
- const error = result.error;
22059
- const status = response.statusCode ?? 503;
22060
- const codes = [
22061
- "invalid_input",
22062
- "unsupported_context",
22063
- "unauthenticated",
22064
- "access_denied",
22065
- "request_too_large",
22066
- "conflict",
22067
- "local_only",
22068
- "integrity",
22069
- "unavailable",
22070
- "capacity",
22071
- "method_not_allowed"
22072
- ];
22073
- if (error?.code && codes.includes(error.code))
22074
- throw new FileOperationError(error.code, status, error.code === "capacity" || error.code === "unavailable");
22075
- throw new FileOperationError("unavailable", 503, true);
22128
+ reject(new FileOperationError("unsupported_context", 400));
22129
+ return;
22130
+ }
22131
+ const headers = new Headers;
22132
+ for (const [key, value] of Object.entries(response.headers))
22133
+ if (value !== undefined)
22134
+ headers.set(key, Array.isArray(value) ? value.join(", ") : value);
22135
+ const source = Readable.toWeb(response, { strategy: {
22136
+ highWaterMark: 65536,
22137
+ size: (chunk) => chunk.byteLength
22138
+ } });
22139
+ const reader = source.getReader();
22140
+ let closed = false;
22141
+ const body = new ReadableStream({
22142
+ async pull(controller) {
22143
+ let timer;
22144
+ try {
22145
+ if (!isCurrent())
22146
+ throw new FileOperationError("unsupported_context", 400);
22147
+ timer = setTimeout(() => response.destroy(new Error("Files delivery stalled")), 30000);
22148
+ const next = await reader.read();
22149
+ if (closed)
22150
+ return;
22151
+ if (next.done) {
22152
+ closed = true;
22153
+ reader.releaseLock();
22154
+ controller.close();
22155
+ } else
22156
+ controller.enqueue(next.value);
22157
+ } catch (error) {
22158
+ closed = true;
22159
+ controller.error(error);
22160
+ await cancelCall().catch(() => {});
22161
+ response.socket?.destroy();
22162
+ response.destroy();
22163
+ await reader.cancel(error).catch(() => {});
22164
+ reader.releaseLock();
22165
+ } finally {
22166
+ clearTimeout(timer);
22076
22167
  }
22077
- if (operation === "data/query") {
22078
- throw dataTransportError(response.statusCode ?? 0, result);
22168
+ },
22169
+ async cancel(reason) {
22170
+ closed = true;
22171
+ await cancelCall().catch(() => {});
22172
+ response.socket?.destroy();
22173
+ response.destroy();
22174
+ call.destroy();
22175
+ await reader.cancel(reason).catch(() => {});
22176
+ reader.releaseLock();
22177
+ }
22178
+ }, { highWaterMark: 0 });
22179
+ resolve(new Response(body, { status: response.statusCode ?? 503, headers }));
22180
+ } catch (error) {
22181
+ response.destroy();
22182
+ reject(error instanceof FileOperationError ? error : new FileOperationError("unavailable", 503, true));
22183
+ }
22184
+ });
22185
+ call.on("error", (error) => {
22186
+ cancelCall().catch(() => {});
22187
+ reject(error instanceof FileOperationError ? error : new FileOperationError("unavailable", 503, true));
22188
+ });
22189
+ call.on("timeout", () => call.destroy(new Error("Files I/O stalled")));
22190
+ call.on("information", () => call.setTimeout(30000));
22191
+ if (init.body instanceof ReadableStream) {
22192
+ call.flushHeaders();
22193
+ const reader = sourceReader = init.body.getReader();
22194
+ const abort = () => {
22195
+ reader.cancel(signal.reason).catch(() => {});
22196
+ };
22197
+ signal.addEventListener("abort", abort, { once: true });
22198
+ call.once("close", abort);
22199
+ pumping = (async () => {
22200
+ try {
22201
+ for (;; ) {
22202
+ const next = await reader.read();
22203
+ if (next.done)
22204
+ break;
22205
+ if (!(next.value instanceof Uint8Array))
22206
+ throw new FileOperationError("invalid_input", 400);
22207
+ if (!isCurrent())
22208
+ throw new FileOperationError("unsupported_context", 400);
22209
+ for (let offset = 0;offset < next.value.byteLength; offset += 262144) {
22210
+ if (call.destroyed)
22211
+ throw new Error("Files parent disconnected");
22212
+ await new Promise((done, fail3) => {
22213
+ const closed = () => fail3(new Error("Files parent disconnected"));
22214
+ call.once("close", closed);
22215
+ call.write(next.value.subarray(offset, offset + 262144), (error) => {
22216
+ call.removeListener("close", closed);
22217
+ error ? fail3(error) : done();
22218
+ });
22219
+ });
22079
22220
  }
22080
- throw new Error(typeof result.error === "string" ? result.error : "Preparation failed");
22081
22221
  }
22082
- resolve(result);
22222
+ call.end();
22083
22223
  } catch (error) {
22084
- reject(error);
22224
+ call.destroy(error instanceof Error ? error : new Error("Files transport failed"));
22225
+ } finally {
22226
+ await reader.cancel().catch(() => {});
22227
+ signal.removeEventListener("abort", abort);
22228
+ call.removeListener("close", abort);
22229
+ reader.releaseLock();
22085
22230
  }
22086
- });
22087
- });
22088
- call.on("error", () => reject(new Error("Preparation transport unavailable; nothing was published")));
22089
- call.on("timeout", () => call.destroy(new Error("Preparation timed out")));
22090
- call.end(JSON.stringify(input));
22231
+ })();
22232
+ } else
22233
+ call.end(init.body === undefined ? undefined : Buffer.from(init.body));
22091
22234
  });
22235
+ try {
22236
+ return await result;
22237
+ } finally {
22238
+ await pumping;
22239
+ }
22092
22240
  };
22241
+ return { request, files, isCurrent, identity: `private:${socket}` };
22242
+ }
22243
+ function localRoutineRequest(environment = typeof __ODDINGTON_BROWSER__ !== "undefined" && __ODDINGTON_BROWSER__ ? {} : typeof process === "undefined" ? {} : process.env) {
22244
+ if (typeof __ODDINGTON_BROWSER__ === "undefined" || !__ODDINGTON_BROWSER__) {
22245
+ const {
22246
+ ODDINGTON_ROUTINE_LOCAL: mode,
22247
+ ODDINGTON_ROUTINE_LOCAL_SOCKET: socket,
22248
+ ODDINGTON_ROUTINE_LOCAL_TOKEN: token
22249
+ } = environment;
22250
+ if (mode === undefined && socket === undefined && token === undefined)
22251
+ return;
22252
+ if (mode !== "1" || !socket || !token || !/^[0-9a-f]{64}$/.test(token))
22253
+ throw new Error("Preparation transport unavailable; nothing can publish");
22254
+ const tcp = /^tcp:\/\/127\.0\.0\.1:([1-9][0-9]{0,4})$/.exec(socket);
22255
+ const endpoint = tcp && tcp[0] === socket && Number(tcp[1]) <= 65535 ? { hostname: "127.0.0.1", port: Number(tcp[1]) } : socket.startsWith("/") && !socket.startsWith("//") && !socket.includes("\x00") ? { socketPath: socket } : undefined;
22256
+ if (!endpoint)
22257
+ throw new Error("Preparation transport unavailable; nothing can publish");
22258
+ return async (operation, input) => {
22259
+ const { request } = await import("node:http");
22260
+ return await new Promise((resolve, reject) => {
22261
+ const call = request({
22262
+ ...endpoint,
22263
+ path: `/${operation}`,
22264
+ method: "POST",
22265
+ headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
22266
+ timeout: 120000
22267
+ }, (response) => {
22268
+ const bytes = Buffer.alloc(33554432);
22269
+ let size = 0;
22270
+ response.on("data", (chunk) => {
22271
+ size += chunk.length;
22272
+ if (size > 33554432) {
22273
+ response.destroy();
22274
+ reject(new Error("Local result exceeds supported size"));
22275
+ } else
22276
+ bytes.set(chunk, size - chunk.length);
22277
+ });
22278
+ response.on("error", reject);
22279
+ response.on("end", () => {
22280
+ try {
22281
+ const result = JSON.parse(bytes.subarray(0, size).toString());
22282
+ if (response.statusCode !== 200) {
22283
+ if (operation === "files/request") {
22284
+ const error = result.error;
22285
+ const status = response.statusCode ?? 503;
22286
+ const codes = [
22287
+ "invalid_input",
22288
+ "unsupported_context",
22289
+ "unauthenticated",
22290
+ "access_denied",
22291
+ "request_too_large",
22292
+ "conflict",
22293
+ "local_only",
22294
+ "integrity",
22295
+ "unavailable",
22296
+ "capacity",
22297
+ "method_not_allowed"
22298
+ ];
22299
+ if (error?.code && codes.includes(error.code))
22300
+ throw new FileOperationError(error.code, status, error.code === "capacity" || error.code === "unavailable");
22301
+ throw new FileOperationError("unavailable", 503, true);
22302
+ }
22303
+ if (operation === "data/query") {
22304
+ throw dataTransportError(response.statusCode ?? 0, result);
22305
+ }
22306
+ throw new Error(typeof result.error === "string" ? result.error : "Preparation failed");
22307
+ }
22308
+ resolve(result);
22309
+ } catch (error) {
22310
+ reject(error);
22311
+ }
22312
+ });
22313
+ });
22314
+ call.on("error", () => reject(new Error("Preparation transport unavailable; nothing was published")));
22315
+ call.on("timeout", () => call.destroy(new Error("Preparation timed out")));
22316
+ call.end(JSON.stringify(input));
22317
+ });
22318
+ };
22319
+ }
22093
22320
  }
22094
22321
 
22095
22322
  // src/data-context.ts
@@ -22116,12 +22343,14 @@ async function resolveAppContext() {
22116
22343
  }
22117
22344
  async function resolveDataContext() {
22118
22345
  try {
22119
- const local = localRoutineRequest();
22120
- if (local)
22121
- return { local };
22122
- const managed = privateRoutineRequest();
22123
- if (managed)
22124
- return { local: managed.request };
22346
+ if (typeof __ODDINGTON_BROWSER__ === "undefined" || !__ODDINGTON_BROWSER__) {
22347
+ const local = localRoutineRequest();
22348
+ if (local)
22349
+ return { local };
22350
+ const managed = privateRoutineRequest();
22351
+ if (managed)
22352
+ return { local: managed.request };
22353
+ }
22125
22354
  const host = memberContext;
22126
22355
  if (host !== undefined) {
22127
22356
  const context = await host();
@@ -22133,25 +22362,31 @@ async function resolveDataContext() {
22133
22362
  throw new Error;
22134
22363
  return checked(context.workspace, context.endpoint, { authorization: `Bearer ${context.accessToken}` });
22135
22364
  }
22136
- const environment = typeof process === "undefined" ? undefined : process.env;
22137
- if (!environment || !/^[0-9a-f]{64}$/.test(environment.ODDINGTON_ROUTINE_RUN ?? ""))
22138
- throw new Error;
22139
- return checked(environment.ODDINGTON_ROUTINE_WORKSPACE, oddingtonOrigin(environment), { "x-oddington-run": environment.ODDINGTON_ROUTINE_RUN });
22365
+ if (typeof __ODDINGTON_BROWSER__ === "undefined" || !__ODDINGTON_BROWSER__) {
22366
+ const environment = typeof process === "undefined" ? undefined : process.env;
22367
+ if (!environment || !/^[0-9a-f]{64}$/.test(environment.ODDINGTON_ROUTINE_RUN ?? ""))
22368
+ throw new Error;
22369
+ return checked(environment.ODDINGTON_ROUTINE_WORKSPACE, oddingtonOrigin(environment), { "x-oddington-run": environment.ODDINGTON_ROUTINE_RUN });
22370
+ }
22371
+ throw new Error;
22140
22372
  } catch {
22141
22373
  throw new DataOperationError("context_unavailable");
22142
22374
  }
22143
22375
  }
22144
22376
  async function resolveFilesContext() {
22145
22377
  try {
22146
- const environment = typeof process === "undefined" ? {} : process.env;
22147
- const local = privateRoutineRequest(environment);
22148
- if (local)
22149
- return {
22150
- url: local.identity,
22151
- headers: {},
22152
- isCurrent: local.isCurrent,
22153
- local: local.request
22154
- };
22378
+ const environment = typeof __ODDINGTON_BROWSER__ !== "undefined" && __ODDINGTON_BROWSER__ ? {} : typeof process === "undefined" ? {} : process.env;
22379
+ if (typeof __ODDINGTON_BROWSER__ === "undefined" || !__ODDINGTON_BROWSER__) {
22380
+ const local = privateRoutineRequest(environment);
22381
+ if (local)
22382
+ return {
22383
+ url: local.identity,
22384
+ headers: {},
22385
+ isCurrent: local.isCurrent,
22386
+ local: local.request,
22387
+ localFiles: local.files
22388
+ };
22389
+ }
22155
22390
  const host = memberContext;
22156
22391
  if (host) {
22157
22392
  const context = await host();
@@ -22162,13 +22397,17 @@ async function resolveFilesContext() {
22162
22397
  return {
22163
22398
  ...filesContext(context.workspace, context.endpoint, { authorization: `Bearer ${context.accessToken}` }),
22164
22399
  isCurrent: context.isCurrent,
22165
- local: undefined
22400
+ local: undefined,
22401
+ localFiles: undefined
22166
22402
  };
22167
22403
  }
22168
- if (!/^[0-9a-f]{64}$/.test(environment.ODDINGTON_ROUTINE_RUN ?? ""))
22169
- throw new Error;
22170
- const workspace = environment.ODDINGTON_ROUTINE_WORKSPACE;
22171
- return { ...filesContext(workspace, `https://${workspace}.oddington.ai`, { "x-oddington-run": environment.ODDINGTON_ROUTINE_RUN }), local: undefined };
22404
+ if (typeof __ODDINGTON_BROWSER__ === "undefined" || !__ODDINGTON_BROWSER__) {
22405
+ if (!/^[0-9a-f]{64}$/.test(environment.ODDINGTON_ROUTINE_RUN ?? ""))
22406
+ throw new Error;
22407
+ const workspace = environment.ODDINGTON_ROUTINE_WORKSPACE;
22408
+ return { ...filesContext(workspace, `https://${workspace}.oddington.ai`, { "x-oddington-run": environment.ODDINGTON_ROUTINE_RUN }), local: undefined, localFiles: undefined };
22409
+ }
22410
+ throw new Error;
22172
22411
  } catch {
22173
22412
  throw new FileOperationError("unsupported_context", 400);
22174
22413
  }
@@ -22330,6 +22569,7 @@ var MAX_BYTES = 16 * 1024 * 1024;
22330
22569
  var PART_BYTES = 8 * 1024 * 1024;
22331
22570
  var METADATA_BYTES = 4096;
22332
22571
  var RESULT_BYTES = 16 * 1024;
22572
+ var CONTROL_RESULT_BYTES = 128 * 1024;
22333
22573
  var TIMEOUT_MS = 30000;
22334
22574
  var MAX_ACTIVE = 4;
22335
22575
  var ATTEMPTS = 2;
@@ -22390,15 +22630,15 @@ async function timed(work, parent, timeout = TIMEOUT_MS) {
22390
22630
  parent?.removeEventListener("abort", abort);
22391
22631
  }
22392
22632
  }
22393
- async function result(response) {
22633
+ async function result(response, maximum = RESULT_BYTES) {
22394
22634
  try {
22395
- const bytes = await boundedBytes(response.body, RESULT_BYTES);
22635
+ const bytes = await boundedBytes(response.body, maximum);
22396
22636
  return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
22397
22637
  } catch {
22398
22638
  throw unavailable();
22399
22639
  }
22400
22640
  }
22401
- async function hostedContent(url, init, parent) {
22641
+ async function hostedContent(url, init, parent, upstream = fetch) {
22402
22642
  const delivery = new AbortController;
22403
22643
  const abort = () => delivery.abort(parent.reason);
22404
22644
  parent.addEventListener("abort", abort, { once: true });
@@ -22406,7 +22646,7 @@ async function hostedContent(url, init, parent) {
22406
22646
  abort();
22407
22647
  const detach = () => parent.removeEventListener("abort", abort);
22408
22648
  try {
22409
- const response = await fetch(url, { ...init, signal: delivery.signal });
22649
+ const response = await upstream(url, { ...init, signal: delivery.signal });
22410
22650
  if (delivery.signal.aborted) {
22411
22651
  response.body?.cancel().catch(() => {});
22412
22652
  throw unavailable();
@@ -22482,26 +22722,7 @@ async function send(suffix, init, descriptor, initial, parent) {
22482
22722
  throw new FileOperationError("unsupported_context", 400);
22483
22723
  try {
22484
22724
  return await (async () => {
22485
- const response = context.local ? await (async () => {
22486
- const body = init.body instanceof ArrayBuffer ? new Uint8Array(init.body) : undefined;
22487
- let binary = "";
22488
- if (body)
22489
- for (let offset = 0;offset < body.length; offset += 8192)
22490
- binary += String.fromCharCode(...body.subarray(offset, offset + 8192));
22491
- const value2 = await context.local("files/request", {
22492
- suffix,
22493
- method: init.method,
22494
- headers: Object.fromEntries(new Headers(init.headers)),
22495
- ...body ? { bytes: btoa(binary) } : {}
22496
- });
22497
- if (value2.bytes !== undefined) {
22498
- if (value2.bytes.length > Math.ceil(MAX_BYTES / 3) * 4)
22499
- throw unavailable();
22500
- const data2 = atob(value2.bytes);
22501
- return new Response(Uint8Array.from(data2, (c) => c.charCodeAt(0)), { status: value2.status, headers: value2.headers });
22502
- }
22503
- return Response.json(value2.json, { status: value2.status, headers: value2.headers });
22504
- })() : await (async () => {
22725
+ const response = context.localFiles ? await context.localFiles(suffix, init, parent) : await (async () => {
22505
22726
  const url = `${context.url}${suffix}`;
22506
22727
  const request = {
22507
22728
  ...init,
@@ -22531,7 +22752,7 @@ async function send(suffix, init, descriptor, initial, parent) {
22531
22752
  }
22532
22753
  if (!descriptor)
22533
22754
  return response;
22534
- const value = await result(response);
22755
+ const value = await result(response, descriptor === "control" ? CONTROL_RESULT_BYTES : RESULT_BYTES);
22535
22756
  if (descriptor === "control") {
22536
22757
  checkCurrent();
22537
22758
  return value;
@@ -22551,7 +22772,7 @@ async function send(suffix, init, descriptor, initial, parent) {
22551
22772
  })();
22552
22773
  } catch (error) {
22553
22774
  const failure = error instanceof FileOperationError ? error : unavailable();
22554
- if (!failure.retryable || attempt + 1 === ATTEMPTS)
22775
+ if (init.body instanceof ReadableStream || !failure.retryable || attempt + 1 === ATTEMPTS)
22555
22776
  throw failure;
22556
22777
  }
22557
22778
  }
@@ -22562,7 +22783,10 @@ function idPath(id2) {
22562
22783
  throw invalid2();
22563
22784
  return `/${id2}`;
22564
22785
  }
22565
- async function multipartCreate(input, metadata, context) {
22786
+ async function multipartCreate(input, metadata, exchange, release = () => {
22787
+ active--;
22788
+ }, upload) {
22789
+ const route = upload ? `${idPath(upload.file.id)}/upload/multipart` : "/multipart";
22566
22790
  let ticket;
22567
22791
  let reader;
22568
22792
  let chunk, offset = 0;
@@ -22580,11 +22804,11 @@ async function multipartCreate(input, metadata, context) {
22580
22804
  body: JSON.stringify(body)
22581
22805
  });
22582
22806
  try {
22583
- const start = await request((signal) => send("/multipart/start", control(metadata), "control", context, signal));
22807
+ const start = await request((signal) => exchange(`${route}/start`, control(upload?.file ?? metadata), "control", signal));
22584
22808
  if (!start || typeof start !== "object" || Array.isArray(start))
22585
22809
  throw unavailable();
22586
22810
  const session = start;
22587
- if (Object.keys(session).sort().join(",") !== "partBytes,ticket" || session.partBytes !== PART_BYTES || typeof session.ticket !== "string" || !session.ticket || session.ticket.length > 8192)
22811
+ if (Object.keys(session).sort().join(",") !== "partBytes,ticket" || session.partBytes !== PART_BYTES || typeof session.ticket !== "string" || !session.ticket || session.ticket.length > 64 * 1024)
22588
22812
  throw unavailable();
22589
22813
  ticket = session.ticket;
22590
22814
  if (input.file instanceof ReadableStream)
@@ -22603,7 +22827,7 @@ async function multipartCreate(input, metadata, context) {
22603
22827
  throw error;
22604
22828
  }
22605
22829
  };
22606
- for (let sent = 0, partNumber = 1;sent < metadata.size; partNumber++) {
22830
+ for (let sent = 0, partNumber = 1;sent < metadata.size || metadata.size === 0 && partNumber === 1; partNumber++) {
22607
22831
  const length = Math.min(PART_BYTES, metadata.size - sent);
22608
22832
  let part;
22609
22833
  if (input.file instanceof Uint8Array)
@@ -22649,11 +22873,12 @@ async function multipartCreate(input, metadata, context) {
22649
22873
  throw invalid2();
22650
22874
  }
22651
22875
  }
22652
- const value = await request((signal) => send("/multipart/part", {
22876
+ const value = await request((signal) => exchange(`${route}/part`, {
22653
22877
  method: "POST",
22654
22878
  headers: { "content-type": "application/octet-stream", "x-oddington-transfer": ticket, "x-oddington-part": String(partNumber) },
22655
22879
  body: part.buffer
22656
- }, "control", context, signal));
22880
+ }, "control", signal));
22881
+ part = undefined;
22657
22882
  const receipt = value;
22658
22883
  if (!receipt || typeof receipt !== "object" || Object.keys(receipt).sort().join(",") !== "etag,partNumber" || receipt.partNumber !== partNumber || typeof receipt.etag !== "string" || !receipt.etag || receipt.etag.length > 256 || /[\u0000-\u001f\u007f]/.test(receipt.etag))
22659
22884
  throw unavailable();
@@ -22661,25 +22886,35 @@ async function multipartCreate(input, metadata, context) {
22661
22886
  sent += length;
22662
22887
  lastProgress = Date.now();
22663
22888
  }
22889
+ if (upload)
22890
+ for (const name of Object.keys(upload.file.enrichments)) {
22891
+ const bytes = await request(() => upload.enrichment(name));
22892
+ if (bytes.byteLength !== upload.file.enrichments[name].size || bytes.byteLength > MAX_BYTES)
22893
+ throw invalid2();
22894
+ await request((signal) => exchange(`${route}/enrichments/${encodeURIComponent(name)}`, {
22895
+ method: "POST",
22896
+ headers: { "content-type": "application/octet-stream", "x-oddington-transfer": ticket },
22897
+ body: bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)
22898
+ }, "control", signal));
22899
+ }
22900
+ await upload?.beforeComplete();
22664
22901
  const completion = control({ parts: receipts });
22665
22902
  if (new TextEncoder().encode(completion.body).byteLength > 128 * 1024)
22666
22903
  throw unavailable();
22667
- return await request((signal) => send("/multipart/complete", completion, true, context, signal), TIMEOUT_MS + Math.ceil(metadata.size / (1024 * 1024)) * 1000);
22904
+ return parseFileDescriptor(await request((signal) => exchange(`${route}/complete`, completion, true, signal), TIMEOUT_MS + Math.ceil(metadata.size / (1024 * 1024)) * 1000));
22668
22905
  } catch (error) {
22669
22906
  if (reader)
22670
22907
  await reader.cancel(error).catch(() => {});
22671
22908
  if (ticket)
22672
- await request((signal) => send("/multipart/abort", control({}), "control", context, signal)).catch(() => {});
22909
+ await request((signal) => exchange(`${route}/abort`, control({}), "control", signal)).catch(() => {});
22673
22910
  throw error instanceof FileOperationError ? error : unavailable();
22674
22911
  } finally {
22675
22912
  chunk = undefined;
22676
22913
  reader?.releaseLock();
22677
22914
  if (pending.size)
22678
- Promise.allSettled([...pending]).then(() => {
22679
- active--;
22680
- });
22915
+ Promise.allSettled([...pending]).then(release);
22681
22916
  else
22682
- active--;
22917
+ release();
22683
22918
  }
22684
22919
  }
22685
22920
  async function operation(work, initial, parent) {
@@ -22687,6 +22922,13 @@ async function operation(work, initial, parent) {
22687
22922
  if (active >= MAX_ACTIVE)
22688
22923
  throw new FileOperationError("capacity", 429, true);
22689
22924
  active++;
22925
+ if (context.localFiles) {
22926
+ try {
22927
+ return await work(context, parent ?? new AbortController().signal);
22928
+ } finally {
22929
+ active--;
22930
+ }
22931
+ }
22690
22932
  return timed(async (signal) => {
22691
22933
  try {
22692
22934
  return await work(context, signal);
@@ -22776,7 +23018,7 @@ var files = {
22776
23018
  storage: input.storage,
22777
23019
  csv: input.csv,
22778
23020
  requestKey: options?.requestKey === undefined ? crypto.randomUUID() : options.requestKey
22779
- }, context.local ? MAX_BYTES : FILE_ORIGINAL_MAX_BYTES);
23021
+ }, FILE_ORIGINAL_MAX_BYTES);
22780
23022
  if (!context.local && metadata.storage === "local")
22781
23023
  throw new FileOperationError("unsupported_context", 400);
22782
23024
  const header = encodeURIComponent(JSON.stringify(metadata));
@@ -22785,8 +23027,32 @@ var files = {
22785
23027
  if (active >= MAX_ACTIVE)
22786
23028
  throw new FileOperationError("capacity", 429, true);
22787
23029
  active++;
23030
+ if (context.localFiles && metadata.size > MAX_BYTES) {
23031
+ const controller = new AbortController;
23032
+ const file = input.file;
23033
+ let offset = 0;
23034
+ const body = file instanceof ReadableStream ? file : file instanceof Blob ? file.stream() : new ReadableStream({
23035
+ pull(controller2) {
23036
+ if (offset === file.byteLength) {
23037
+ controller2.close();
23038
+ return;
23039
+ }
23040
+ const end = Math.min(offset + 256 * 1024, file.byteLength);
23041
+ controller2.enqueue(file.subarray(offset, end));
23042
+ offset = end;
23043
+ }
23044
+ }, { highWaterMark: 0 });
23045
+ try {
23046
+ return await send("", { method: "POST", headers: {
23047
+ "content-type": "application/octet-stream",
23048
+ "x-oddington-file": header
23049
+ }, body }, true, context, controller.signal);
23050
+ } finally {
23051
+ active--;
23052
+ }
23053
+ }
22788
23054
  if (metadata.size > MAX_BYTES)
22789
- return multipartCreate(input, metadata, context);
23055
+ return multipartCreate(input, metadata, (suffix, init, kind, signal) => kind === true ? send(suffix, init, true, context, signal) : send(suffix, init, "control", context, signal));
22790
23056
  return timed(async (signal) => {
22791
23057
  try {
22792
23058
  const bytes = array2 ? input.file.slice() : await boundedBytes(blob ? input.file.stream() : input.file, metadata.size);
@@ -23561,7 +23827,7 @@ function freeze(value) {
23561
23827
  }
23562
23828
  return value;
23563
23829
  }
23564
- if (typeof process !== "undefined" && process.env.ODDINGTON_APP_RUN !== undefined) {
23830
+ if ((typeof __ODDINGTON_BROWSER__ === "undefined" || !__ODDINGTON_BROWSER__) && typeof process !== "undefined" && process.env.ODDINGTON_APP_RUN !== undefined) {
23565
23831
  let fail4 = function() {
23566
23832
  broken = true;
23567
23833
  pending?.reject(unavailable2());
@@ -23712,13 +23978,13 @@ init_inbox_contract();
23712
23978
  init_inbox_contract();
23713
23979
  init_inbox_files();
23714
23980
  function routinePublicationClient(options = {}) {
23715
- const environment = typeof process === "undefined" ? {} : process.env;
23981
+ const environment = typeof __ODDINGTON_BROWSER__ !== "undefined" && __ODDINGTON_BROWSER__ ? {} : typeof process === "undefined" ? {} : process.env;
23716
23982
  const local = localRoutineRequest(environment);
23717
23983
  const workspace = options.workspace ?? environment.ODDINGTON_ROUTINE_WORKSPACE;
23718
23984
  const capability = options.capability ?? environment.ODDINGTON_ROUTINE_RUN;
23719
23985
  if (!workspace || !/^[a-z0-9][a-z0-9-]*$/.test(workspace) || capability !== undefined && !/^[0-9a-f]{64}$/.test(capability))
23720
23986
  throw new Error("A workspace and valid optional routine capability are required");
23721
- const workingRoot = options.workingRoot ?? environment.ODDINGTON_INBOX_WORKING_ROOT ?? (typeof process === "undefined" ? undefined : process.cwd());
23987
+ const workingRoot = options.workingRoot ?? environment.ODDINGTON_INBOX_WORKING_ROOT ?? (typeof __ODDINGTON_BROWSER__ !== "undefined" && __ODDINGTON_BROWSER__ ? undefined : typeof process === "undefined" ? undefined : process.cwd());
23722
23988
  const headers = capability ? { "x-oddington-run": capability } : {};
23723
23989
  const request = options.request ?? (capability ? async (path, init2) => {
23724
23990
  const origin = oddingtonOrigin();
@@ -23783,22 +24049,25 @@ function routinePublicationClient(options = {}) {
23783
24049
  return validateInboxFile(await local("files/add", input));
23784
24050
  if (!workingRoot)
23785
24051
  throw new Error("Inbox file reads require a working root");
23786
- const { readInboxPublicationFile: readInboxPublicationFile2 } = await Promise.resolve().then(() => (init_inbox_publication_file(), exports_inbox_publication_file));
23787
- const bytes = await readInboxPublicationFile2(workingRoot, input);
23788
- const name = input.name ?? input.path.split("/").at(-1);
23789
- const response = await request(`/api/cli/workspaces/${workspace}/inbox/files/upload`, {
23790
- method: "POST",
23791
- headers: {
23792
- "content-type": "application/octet-stream",
23793
- ...headers,
23794
- "x-oddington-file": JSON.stringify({ name, ...input.csv ? { csv: input.csv } : {} })
23795
- },
23796
- body: Uint8Array.from(bytes).buffer
23797
- });
23798
- const result2 = await response.json();
23799
- if (!response.ok)
23800
- throw new Error(result2.error ?? "Could not retain file");
23801
- return await verifyInboxFile(bytes, validateInboxFile(result2));
24052
+ if (typeof __ODDINGTON_BROWSER__ === "undefined" || !__ODDINGTON_BROWSER__) {
24053
+ const { readInboxPublicationFile: readInboxPublicationFile2 } = await Promise.resolve().then(() => (init_inbox_publication_file(), exports_inbox_publication_file));
24054
+ const bytes = await readInboxPublicationFile2(workingRoot, input);
24055
+ const name = input.name ?? input.path.split("/").at(-1);
24056
+ const response = await request(`/api/cli/workspaces/${workspace}/inbox/files/upload`, {
24057
+ method: "POST",
24058
+ headers: {
24059
+ "content-type": "application/octet-stream",
24060
+ ...headers,
24061
+ "x-oddington-file": JSON.stringify({ name, ...input.csv ? { csv: input.csv } : {} })
24062
+ },
24063
+ body: Uint8Array.from(bytes).buffer
24064
+ });
24065
+ const result2 = await response.json();
24066
+ if (!response.ok)
24067
+ throw new Error(result2.error ?? "Could not retain file");
24068
+ return await verifyInboxFile(bytes, validateInboxFile(result2));
24069
+ }
24070
+ throw new Error("Inbox file reads require a Node working root");
23802
24071
  }
23803
24072
  },
23804
24073
  async check(body2) {