@oddington/sdk 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -10
- package/dist/browser.js +2150 -0
- package/dist/index.js +732 -195
- package/package.json +3 -1
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
|
-
|
|
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
|
-
|
|
22017
|
-
|
|
22018
|
-
|
|
22019
|
-
const {
|
|
22020
|
-
|
|
22021
|
-
|
|
22022
|
-
|
|
22023
|
-
|
|
22024
|
-
|
|
22025
|
-
|
|
22026
|
-
|
|
22027
|
-
|
|
22028
|
-
|
|
22029
|
-
|
|
22030
|
-
|
|
22031
|
-
|
|
22032
|
-
|
|
22033
|
-
|
|
22034
|
-
|
|
22035
|
-
|
|
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 || suffix === "" && init.method === "POST") {
|
|
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:
|
|
22088
|
+
path: "/files/cancel",
|
|
22038
22089
|
method: "POST",
|
|
22039
|
-
|
|
22040
|
-
|
|
22090
|
+
timeout: 30000,
|
|
22091
|
+
headers: { authorization: `Bearer ${token}`, "x-oddington-files-call": id2 }
|
|
22041
22092
|
}, (response) => {
|
|
22042
|
-
|
|
22043
|
-
|
|
22044
|
-
response.
|
|
22045
|
-
|
|
22046
|
-
|
|
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
|
|
22049
|
-
|
|
22050
|
-
|
|
22051
|
-
|
|
22052
|
-
|
|
22053
|
-
|
|
22054
|
-
|
|
22055
|
-
|
|
22056
|
-
|
|
22057
|
-
|
|
22058
|
-
|
|
22059
|
-
|
|
22060
|
-
|
|
22061
|
-
|
|
22062
|
-
|
|
22063
|
-
|
|
22064
|
-
|
|
22065
|
-
|
|
22066
|
-
"
|
|
22067
|
-
|
|
22068
|
-
|
|
22069
|
-
|
|
22070
|
-
|
|
22071
|
-
|
|
22072
|
-
|
|
22073
|
-
|
|
22074
|
-
|
|
22075
|
-
|
|
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
|
-
|
|
22078
|
-
|
|
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
|
-
|
|
22222
|
+
call.end();
|
|
22083
22223
|
} catch (error) {
|
|
22084
|
-
|
|
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
|
-
|
|
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
|
-
|
|
22120
|
-
|
|
22121
|
-
|
|
22122
|
-
|
|
22123
|
-
|
|
22124
|
-
|
|
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
|
-
|
|
22137
|
-
|
|
22138
|
-
|
|
22139
|
-
|
|
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
|
-
|
|
22148
|
-
|
|
22149
|
-
|
|
22150
|
-
|
|
22151
|
-
|
|
22152
|
-
|
|
22153
|
-
|
|
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 (
|
|
22169
|
-
|
|
22170
|
-
|
|
22171
|
-
|
|
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,
|
|
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
|
|
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.
|
|
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,
|
|
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) =>
|
|
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 >
|
|
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) =>
|
|
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",
|
|
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) =>
|
|
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) =>
|
|
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
|
-
|
|
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
|
-
},
|
|
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);
|
|
@@ -22848,9 +23114,16 @@ var files = {
|
|
|
22848
23114
|
};
|
|
22849
23115
|
// src/app-context.ts
|
|
22850
23116
|
var sessionKey = "_os.app.session";
|
|
23117
|
+
var responseChecks = new WeakMap;
|
|
22851
23118
|
|
|
22852
23119
|
class AppResponseReadError extends Error {
|
|
22853
23120
|
}
|
|
23121
|
+
|
|
23122
|
+
class AppContextDenied extends Error {
|
|
23123
|
+
}
|
|
23124
|
+
|
|
23125
|
+
class AppRenewalUnavailable extends Error {
|
|
23126
|
+
}
|
|
22854
23127
|
async function appResponse(response, maximum, signal) {
|
|
22855
23128
|
if (signal.aborted || !response.body || response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() !== "application/json")
|
|
22856
23129
|
throw new Error;
|
|
@@ -22871,8 +23144,13 @@ async function appResponse(response, maximum, signal) {
|
|
|
22871
23144
|
}
|
|
22872
23145
|
if (signal.aborted)
|
|
22873
23146
|
throw new Error;
|
|
22874
|
-
if (chunk.done)
|
|
22875
|
-
|
|
23147
|
+
if (chunk.done) {
|
|
23148
|
+
const value = JSON.parse(text3 + decoder.decode());
|
|
23149
|
+
await responseChecks.get(response)?.(value);
|
|
23150
|
+
if (signal.aborted)
|
|
23151
|
+
throw new Error;
|
|
23152
|
+
return value;
|
|
23153
|
+
}
|
|
22876
23154
|
bytes += chunk.value.byteLength;
|
|
22877
23155
|
if (bytes > maximum) {
|
|
22878
23156
|
abort();
|
|
@@ -22899,38 +23177,239 @@ function appContext(origin, active2) {
|
|
|
22899
23177
|
let denied = false;
|
|
22900
23178
|
const controller = new AbortController;
|
|
22901
23179
|
let expiry;
|
|
22902
|
-
|
|
22903
|
-
|
|
23180
|
+
let startup;
|
|
23181
|
+
let renewalTimer;
|
|
23182
|
+
let renewal;
|
|
23183
|
+
let observation;
|
|
23184
|
+
let resolution2;
|
|
23185
|
+
let disposed = false;
|
|
23186
|
+
const stored = () => globalThis.localStorage.getItem(sessionKey);
|
|
23187
|
+
const alive = () => {
|
|
23188
|
+
try {
|
|
23189
|
+
return !denied && !disposed && active2() && globalThis.location.origin === origin && (!binding || binding.expiresAt > Date.now());
|
|
23190
|
+
} catch {
|
|
23191
|
+
return false;
|
|
23192
|
+
}
|
|
23193
|
+
};
|
|
23194
|
+
const cancel = () => {
|
|
22904
23195
|
controller.abort();
|
|
22905
23196
|
clearTimeout(expiry);
|
|
23197
|
+
clearTimeout(startup);
|
|
23198
|
+
clearTimeout(renewalTimer);
|
|
22906
23199
|
clearInterval(monitor);
|
|
23200
|
+
globalThis.removeEventListener?.("storage", storageChanged);
|
|
23201
|
+
};
|
|
23202
|
+
const invalidate = () => {
|
|
23203
|
+
denied = true;
|
|
23204
|
+
cancel();
|
|
22907
23205
|
try {
|
|
22908
|
-
if (active2() && globalThis.location.origin === origin && (!credential || globalThis.localStorage.getItem(sessionKey) === credential)) {
|
|
23206
|
+
if (!disposed && active2() && globalThis.location.origin === origin && (!credential || globalThis.localStorage.getItem(sessionKey) === credential)) {
|
|
22909
23207
|
globalThis.localStorage.removeItem(sessionKey);
|
|
22910
23208
|
}
|
|
22911
23209
|
} catch {}
|
|
22912
23210
|
};
|
|
22913
23211
|
const current = () => {
|
|
22914
23212
|
try {
|
|
22915
|
-
return
|
|
23213
|
+
return alive() && stored() === credential;
|
|
22916
23214
|
} catch {
|
|
22917
23215
|
return false;
|
|
22918
23216
|
}
|
|
22919
23217
|
};
|
|
22920
|
-
const
|
|
22921
|
-
|
|
23218
|
+
const sameSession = (value) => !binding || value.appId === binding.appId && value.memberId === binding.memberId && value.workspace === binding.workspace && value.sessionId === binding.sessionId;
|
|
23219
|
+
const bounded = (value) => validBinding(value) && value.expiresAt > Date.now() && value.expiresAt <= Date.now() + 600000 && sameSession(value);
|
|
23220
|
+
async function proof(candidate, signal = controller.signal) {
|
|
23221
|
+
if (!alive() || !/^[A-Za-z0-9_-]{43}$/.test(candidate))
|
|
23222
|
+
throw new Error;
|
|
23223
|
+
const url = `${origin}/_os/app/context`;
|
|
23224
|
+
const response = await fetch(url, {
|
|
23225
|
+
credentials: "omit",
|
|
23226
|
+
redirect: "error",
|
|
23227
|
+
cache: "no-store",
|
|
23228
|
+
referrerPolicy: "no-referrer",
|
|
23229
|
+
headers: { authorization: `Bearer ${candidate}` },
|
|
23230
|
+
signal
|
|
23231
|
+
});
|
|
23232
|
+
if (response.redirected || response.url && response.url !== url)
|
|
23233
|
+
throw new Error;
|
|
23234
|
+
if (response.status === 401 || response.status === 403)
|
|
23235
|
+
throw new AppContextDenied;
|
|
23236
|
+
if (!response.ok)
|
|
23237
|
+
throw new Error;
|
|
23238
|
+
const value = await appResponse(response, 4096, signal);
|
|
23239
|
+
if (!alive() || !bounded(value))
|
|
23240
|
+
throw new Error;
|
|
23241
|
+
return value;
|
|
23242
|
+
}
|
|
23243
|
+
function install(value, candidate) {
|
|
23244
|
+
binding = { ...value };
|
|
23245
|
+
credential = candidate;
|
|
23246
|
+
clearTimeout(expiry);
|
|
23247
|
+
clearTimeout(renewalTimer);
|
|
23248
|
+
const remaining = binding.expiresAt - Date.now();
|
|
23249
|
+
expiry = setTimeout(invalidate, remaining);
|
|
23250
|
+
renewalTimer = setTimeout(() => {
|
|
23251
|
+
renew();
|
|
23252
|
+
}, remaining - Math.min(60000, remaining / 5));
|
|
23253
|
+
}
|
|
23254
|
+
async function synchronize() {
|
|
23255
|
+
if (!alive())
|
|
23256
|
+
throw new Error;
|
|
23257
|
+
if (!credential)
|
|
23258
|
+
return;
|
|
23259
|
+
if (stored() === credential)
|
|
23260
|
+
return;
|
|
23261
|
+
if (!binding)
|
|
23262
|
+
throw new Error;
|
|
23263
|
+
if (observation)
|
|
23264
|
+
return observation;
|
|
23265
|
+
observation = (async () => {
|
|
23266
|
+
while (alive()) {
|
|
23267
|
+
const candidate = stored();
|
|
23268
|
+
if (!candidate)
|
|
23269
|
+
throw new Error;
|
|
23270
|
+
let value;
|
|
23271
|
+
try {
|
|
23272
|
+
value = await proof(candidate);
|
|
23273
|
+
} catch {
|
|
23274
|
+
if (stored() !== candidate)
|
|
23275
|
+
continue;
|
|
23276
|
+
throw new Error;
|
|
23277
|
+
}
|
|
23278
|
+
if (stored() !== candidate)
|
|
23279
|
+
continue;
|
|
23280
|
+
install(value, candidate);
|
|
23281
|
+
return;
|
|
23282
|
+
}
|
|
23283
|
+
throw new Error;
|
|
23284
|
+
})();
|
|
23285
|
+
try {
|
|
23286
|
+
await observation;
|
|
23287
|
+
} catch {
|
|
23288
|
+
invalidate();
|
|
23289
|
+
throw new Error("Local app context unavailable.");
|
|
23290
|
+
} finally {
|
|
23291
|
+
observation = undefined;
|
|
23292
|
+
}
|
|
23293
|
+
}
|
|
23294
|
+
async function renew() {
|
|
23295
|
+
if (renewal || !binding || !alive())
|
|
23296
|
+
return;
|
|
23297
|
+
renewal = (async () => {
|
|
23298
|
+
const deadline = binding.expiresAt;
|
|
23299
|
+
const requestController = new AbortController;
|
|
23300
|
+
const abort = () => requestController.abort();
|
|
23301
|
+
controller.signal.addEventListener("abort", abort, { once: true });
|
|
23302
|
+
const timeout = setTimeout(abort, Math.min(20000, deadline - Date.now()));
|
|
23303
|
+
const previous = credential;
|
|
23304
|
+
try {
|
|
23305
|
+
await synchronize();
|
|
23306
|
+
if (credential !== previous)
|
|
23307
|
+
return;
|
|
23308
|
+
const url = `${origin}/_os/app/renew`;
|
|
23309
|
+
let response;
|
|
23310
|
+
try {
|
|
23311
|
+
response = await fetch(url, {
|
|
23312
|
+
method: "POST",
|
|
23313
|
+
credentials: "omit",
|
|
23314
|
+
redirect: "error",
|
|
23315
|
+
cache: "no-store",
|
|
23316
|
+
referrerPolicy: "no-referrer",
|
|
23317
|
+
signal: requestController.signal,
|
|
23318
|
+
headers: { authorization: `Bearer ${previous}` }
|
|
23319
|
+
});
|
|
23320
|
+
} catch {
|
|
23321
|
+
throw new AppRenewalUnavailable;
|
|
23322
|
+
}
|
|
23323
|
+
if (response.redirected || response.url && response.url !== url)
|
|
23324
|
+
throw new Error;
|
|
23325
|
+
const value = await appResponse(response, 4096, requestController.signal);
|
|
23326
|
+
if (response.status === 503 && value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).join(",") === "error" && "error" in value && value.error === "renewal_unavailable")
|
|
23327
|
+
throw new AppRenewalUnavailable;
|
|
23328
|
+
if (response.status === 403 && value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).join(",") === "error" && "error" in value && value.error === "renewal_conflict") {
|
|
23329
|
+
while (alive() && stored() === previous && !requestController.signal.aborted) {
|
|
23330
|
+
await new Promise((resolve2, reject) => {
|
|
23331
|
+
const abortWait = () => {
|
|
23332
|
+
clearTimeout(timer);
|
|
23333
|
+
reject(new Error);
|
|
23334
|
+
};
|
|
23335
|
+
const timer = setTimeout(() => {
|
|
23336
|
+
requestController.signal.removeEventListener("abort", abortWait);
|
|
23337
|
+
resolve2();
|
|
23338
|
+
}, 100);
|
|
23339
|
+
requestController.signal.addEventListener("abort", abortWait, { once: true });
|
|
23340
|
+
});
|
|
23341
|
+
}
|
|
23342
|
+
if (requestController.signal.aborted)
|
|
23343
|
+
throw new Error;
|
|
23344
|
+
await synchronize();
|
|
23345
|
+
return;
|
|
23346
|
+
}
|
|
23347
|
+
if (!response.ok)
|
|
23348
|
+
throw new Error;
|
|
23349
|
+
if (!alive() || Date.now() >= deadline || !value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "context,credential")
|
|
23350
|
+
throw new Error;
|
|
23351
|
+
const result2 = value;
|
|
23352
|
+
if (!bounded(result2.context) || typeof result2.credential !== "string" || !/^[A-Za-z0-9_-]{43}$/.test(result2.credential) || result2.credential === previous)
|
|
23353
|
+
throw new Error;
|
|
23354
|
+
if (stored() !== previous) {
|
|
23355
|
+
await synchronize();
|
|
23356
|
+
return;
|
|
23357
|
+
}
|
|
23358
|
+
globalThis.localStorage.setItem(sessionKey, result2.credential);
|
|
23359
|
+
install(result2.context, result2.credential);
|
|
23360
|
+
} catch (error) {
|
|
23361
|
+
try {
|
|
23362
|
+
if (alive() && stored() && stored() !== previous) {
|
|
23363
|
+
await synchronize();
|
|
23364
|
+
return;
|
|
23365
|
+
}
|
|
23366
|
+
} catch {}
|
|
23367
|
+
try {
|
|
23368
|
+
if (alive() && stored() && stored() !== previous) {
|
|
23369
|
+
await synchronize();
|
|
23370
|
+
return;
|
|
23371
|
+
}
|
|
23372
|
+
} catch {}
|
|
23373
|
+
if ((error instanceof AppRenewalUnavailable || error instanceof AppResponseReadError || requestController.signal.aborted) && current() && Date.now() < deadline) {
|
|
23374
|
+
renewalTimer = setTimeout(() => {
|
|
23375
|
+
renew();
|
|
23376
|
+
}, Math.min(5000, (deadline - Date.now()) / 5));
|
|
23377
|
+
} else
|
|
23378
|
+
invalidate();
|
|
23379
|
+
} finally {
|
|
23380
|
+
clearTimeout(timeout);
|
|
23381
|
+
controller.signal.removeEventListener("abort", abort);
|
|
23382
|
+
}
|
|
23383
|
+
})();
|
|
23384
|
+
try {
|
|
23385
|
+
await renewal;
|
|
23386
|
+
} finally {
|
|
23387
|
+
renewal = undefined;
|
|
23388
|
+
}
|
|
23389
|
+
}
|
|
23390
|
+
const storageChanged = () => {
|
|
23391
|
+
if (!credential)
|
|
23392
|
+
return;
|
|
23393
|
+
if (!alive()) {
|
|
22922
23394
|
invalidate();
|
|
23395
|
+
return;
|
|
23396
|
+
}
|
|
23397
|
+
synchronize().catch(invalidate);
|
|
23398
|
+
};
|
|
23399
|
+
const monitor = setInterval(() => {
|
|
23400
|
+
storageChanged();
|
|
22923
23401
|
}, 100);
|
|
23402
|
+
globalThis.addEventListener?.("storage", storageChanged);
|
|
22924
23403
|
const dispose = () => {
|
|
22925
|
-
|
|
22926
|
-
|
|
22927
|
-
clearInterval(monitor);
|
|
23404
|
+
disposed = true;
|
|
23405
|
+
cancel();
|
|
22928
23406
|
};
|
|
22929
23407
|
async function requestData(operation2, input) {
|
|
22930
23408
|
try {
|
|
22931
23409
|
if (operation2 !== "data/query" || !current())
|
|
22932
23410
|
throw new Error;
|
|
22933
23411
|
const url = `${origin}/_os/app/data`;
|
|
23412
|
+
const admitted = credential;
|
|
22934
23413
|
const response = await fetch(url, {
|
|
22935
23414
|
method: "POST",
|
|
22936
23415
|
credentials: "omit",
|
|
@@ -22941,13 +23420,20 @@ function appContext(origin, active2) {
|
|
|
22941
23420
|
headers: { "content-type": "application/json", authorization: `Bearer ${credential}` },
|
|
22942
23421
|
body: JSON.stringify(input)
|
|
22943
23422
|
});
|
|
23423
|
+
await synchronize();
|
|
23424
|
+
while (alive() && stored() !== credential)
|
|
23425
|
+
await synchronize();
|
|
22944
23426
|
if (!current() || response.redirected || response.url && response.url !== url)
|
|
22945
23427
|
throw new Error;
|
|
22946
23428
|
if (response.status === 401 || response.status === 403) {
|
|
22947
|
-
|
|
23429
|
+
if (admitted === credential)
|
|
23430
|
+
invalidate();
|
|
22948
23431
|
throw new DataOperationError("access_denied");
|
|
22949
23432
|
}
|
|
22950
23433
|
const result2 = await appResponse(response, Number.MAX_SAFE_INTEGER, controller.signal);
|
|
23434
|
+
await synchronize();
|
|
23435
|
+
while (alive() && stored() !== credential)
|
|
23436
|
+
await synchronize();
|
|
22951
23437
|
if (!current())
|
|
22952
23438
|
throw new Error;
|
|
22953
23439
|
if (!response.ok)
|
|
@@ -22967,6 +23453,7 @@ function appContext(origin, active2) {
|
|
|
22967
23453
|
throw new Error("Local app context unavailable.");
|
|
22968
23454
|
}
|
|
22969
23455
|
const url = `${origin}/_os/app/routines/${encodeURIComponent(key)}/${suffix}`;
|
|
23456
|
+
const admitted = credential;
|
|
22970
23457
|
const response = await fetch(url, {
|
|
22971
23458
|
...init,
|
|
22972
23459
|
credentials: "omit",
|
|
@@ -22976,61 +23463,107 @@ function appContext(origin, active2) {
|
|
|
22976
23463
|
signal: controller.signal,
|
|
22977
23464
|
headers: { ...init.body === undefined ? {} : { "content-type": "application/json" }, authorization: `Bearer ${credential}` }
|
|
22978
23465
|
});
|
|
23466
|
+
await synchronize();
|
|
23467
|
+
while (alive() && stored() !== credential)
|
|
23468
|
+
await synchronize();
|
|
22979
23469
|
if (!current() || response.redirected || response.url && response.url !== url) {
|
|
22980
23470
|
invalidate();
|
|
22981
23471
|
throw new Error("Local app context unavailable.");
|
|
22982
23472
|
}
|
|
22983
|
-
if (response.status === 401 || response.status === 403)
|
|
23473
|
+
if ((response.status === 401 || response.status === 403) && admitted === credential)
|
|
22984
23474
|
invalidate();
|
|
23475
|
+
responseChecks.set(response, async (value) => {
|
|
23476
|
+
await synchronize();
|
|
23477
|
+
while (alive() && stored() !== credential)
|
|
23478
|
+
await synchronize();
|
|
23479
|
+
if (admitted === credential && value && typeof value === "object" && "error" in value && value.error === "authority_unavailable")
|
|
23480
|
+
invalidate();
|
|
23481
|
+
});
|
|
22985
23482
|
return response;
|
|
22986
23483
|
}
|
|
22987
|
-
|
|
23484
|
+
function context() {
|
|
23485
|
+
const issued = credential;
|
|
23486
|
+
return {
|
|
23487
|
+
kind: "local",
|
|
23488
|
+
...binding,
|
|
23489
|
+
get expiresAt() {
|
|
23490
|
+
return binding.expiresAt;
|
|
23491
|
+
},
|
|
23492
|
+
isCurrent: current,
|
|
23493
|
+
signal: controller.signal,
|
|
23494
|
+
invalidate: () => {
|
|
23495
|
+
if (issued === credential)
|
|
23496
|
+
invalidate();
|
|
23497
|
+
},
|
|
23498
|
+
requestRoutine,
|
|
23499
|
+
requestData,
|
|
23500
|
+
requestAvailability: (key) => requestRoutine(key, "availability", { method: "GET" })
|
|
23501
|
+
};
|
|
23502
|
+
}
|
|
23503
|
+
const resolveOnce = async () => {
|
|
23504
|
+
let attempted;
|
|
22988
23505
|
try {
|
|
22989
23506
|
if (denied || !active2() || globalThis.location.origin !== origin)
|
|
22990
23507
|
throw new Error;
|
|
22991
|
-
|
|
22992
|
-
|
|
23508
|
+
if (credential)
|
|
23509
|
+
await synchronize();
|
|
23510
|
+
const candidate = stored();
|
|
23511
|
+
if (!candidate || !/^[A-Za-z0-9_-]{43}$/.test(candidate))
|
|
22993
23512
|
throw new Error;
|
|
22994
|
-
credential =
|
|
23513
|
+
credential = candidate;
|
|
23514
|
+
attempted = candidate;
|
|
22995
23515
|
if (!current())
|
|
22996
23516
|
throw new Error;
|
|
22997
|
-
const
|
|
22998
|
-
|
|
22999
|
-
|
|
23000
|
-
|
|
23001
|
-
|
|
23002
|
-
|
|
23003
|
-
}
|
|
23004
|
-
if (!
|
|
23517
|
+
const value = await proof(candidate);
|
|
23518
|
+
if (stored() !== candidate || credential !== candidate) {
|
|
23519
|
+
if (!binding)
|
|
23520
|
+
binding = { ...value };
|
|
23521
|
+
await synchronize();
|
|
23522
|
+
return context();
|
|
23523
|
+
}
|
|
23524
|
+
if (!current())
|
|
23005
23525
|
throw new Error;
|
|
23006
|
-
|
|
23007
|
-
if (!validBinding(value) || !current() || value.expiresAt <= Date.now() || value.expiresAt > Date.now() + 600000 || binding && (value.appId !== binding.appId || value.memberId !== binding.memberId || value.workspace !== binding.workspace || value.expiresAt > binding.expiresAt))
|
|
23526
|
+
if (binding && value.expiresAt > binding.expiresAt)
|
|
23008
23527
|
throw new Error;
|
|
23009
|
-
binding
|
|
23010
|
-
|
|
23011
|
-
|
|
23012
|
-
|
|
23013
|
-
|
|
23014
|
-
|
|
23015
|
-
|
|
23016
|
-
|
|
23017
|
-
|
|
23018
|
-
|
|
23019
|
-
|
|
23020
|
-
|
|
23021
|
-
|
|
23022
|
-
|
|
23528
|
+
if (!binding || value.expiresAt !== binding.expiresAt)
|
|
23529
|
+
install(value, candidate);
|
|
23530
|
+
return context();
|
|
23531
|
+
} catch (error) {
|
|
23532
|
+
try {
|
|
23533
|
+
if (binding && current() && attempted && attempted !== credential)
|
|
23534
|
+
return context();
|
|
23535
|
+
if (binding && alive() && stored() && stored() !== credential) {
|
|
23536
|
+
await synchronize();
|
|
23537
|
+
return context();
|
|
23538
|
+
}
|
|
23539
|
+
} catch {}
|
|
23540
|
+
if (error instanceof AppContextDenied && binding && alive() && renewal)
|
|
23541
|
+
throw new Error("Local app context unavailable.");
|
|
23023
23542
|
invalidate();
|
|
23024
23543
|
throw new Error("Local app context unavailable.");
|
|
23025
23544
|
}
|
|
23026
23545
|
};
|
|
23027
|
-
|
|
23546
|
+
const resolve = () => {
|
|
23547
|
+
if (resolution2)
|
|
23548
|
+
return resolution2;
|
|
23549
|
+
resolution2 = resolveOnce().finally(() => {
|
|
23550
|
+
resolution2 = undefined;
|
|
23551
|
+
});
|
|
23552
|
+
return resolution2;
|
|
23553
|
+
};
|
|
23554
|
+
const start = () => {
|
|
23555
|
+
startup = setTimeout(() => {
|
|
23556
|
+
if (alive() && !resolution2 && !binding)
|
|
23557
|
+
resolve().catch(() => {});
|
|
23558
|
+
}, 0);
|
|
23559
|
+
};
|
|
23560
|
+
return { resolve, start, invalidate, dispose };
|
|
23028
23561
|
}
|
|
23029
23562
|
function validBinding(value) {
|
|
23030
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "appId,expiresAt,memberId,workspace")
|
|
23563
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "appId,expiresAt,memberId,sessionId,workspace")
|
|
23031
23564
|
return false;
|
|
23032
23565
|
const binding = value;
|
|
23033
|
-
return typeof binding.appId === "string" && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(binding.appId) && typeof binding.workspace === "string" && /^[a-z0-9][a-z0-9-]{0,62}$/.test(binding.workspace) && typeof binding.memberId === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(binding.memberId) && typeof binding.expiresAt === "number" && Number.isFinite(binding.expiresAt);
|
|
23566
|
+
return typeof binding.appId === "string" && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(binding.appId) && typeof binding.workspace === "string" && /^[a-z0-9][a-z0-9-]{0,62}$/.test(binding.workspace) && typeof binding.memberId === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(binding.memberId) && typeof binding.sessionId === "string" && /^[A-Za-z0-9_-]{43}$/.test(binding.sessionId) && typeof binding.expiresAt === "number" && Number.isFinite(binding.expiresAt);
|
|
23034
23567
|
}
|
|
23035
23568
|
|
|
23036
23569
|
// src/os.ts
|
|
@@ -23098,6 +23631,7 @@ function init({ auth }) {
|
|
|
23098
23631
|
installedDispose = undefined;
|
|
23099
23632
|
};
|
|
23100
23633
|
installedDispose = dispose;
|
|
23634
|
+
local?.start();
|
|
23101
23635
|
}
|
|
23102
23636
|
var os = { init };
|
|
23103
23637
|
// src/app-routine-browser.ts
|
|
@@ -23561,7 +24095,7 @@ function freeze(value) {
|
|
|
23561
24095
|
}
|
|
23562
24096
|
return value;
|
|
23563
24097
|
}
|
|
23564
|
-
if (typeof process !== "undefined" && process.env.ODDINGTON_APP_RUN !== undefined) {
|
|
24098
|
+
if ((typeof __ODDINGTON_BROWSER__ === "undefined" || !__ODDINGTON_BROWSER__) && typeof process !== "undefined" && process.env.ODDINGTON_APP_RUN !== undefined) {
|
|
23565
24099
|
let fail4 = function() {
|
|
23566
24100
|
broken = true;
|
|
23567
24101
|
pending?.reject(unavailable2());
|
|
@@ -23712,13 +24246,13 @@ init_inbox_contract();
|
|
|
23712
24246
|
init_inbox_contract();
|
|
23713
24247
|
init_inbox_files();
|
|
23714
24248
|
function routinePublicationClient(options = {}) {
|
|
23715
|
-
const environment = typeof process === "undefined" ? {} : process.env;
|
|
24249
|
+
const environment = typeof __ODDINGTON_BROWSER__ !== "undefined" && __ODDINGTON_BROWSER__ ? {} : typeof process === "undefined" ? {} : process.env;
|
|
23716
24250
|
const local = localRoutineRequest(environment);
|
|
23717
24251
|
const workspace = options.workspace ?? environment.ODDINGTON_ROUTINE_WORKSPACE;
|
|
23718
24252
|
const capability = options.capability ?? environment.ODDINGTON_ROUTINE_RUN;
|
|
23719
24253
|
if (!workspace || !/^[a-z0-9][a-z0-9-]*$/.test(workspace) || capability !== undefined && !/^[0-9a-f]{64}$/.test(capability))
|
|
23720
24254
|
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());
|
|
24255
|
+
const workingRoot = options.workingRoot ?? environment.ODDINGTON_INBOX_WORKING_ROOT ?? (typeof __ODDINGTON_BROWSER__ !== "undefined" && __ODDINGTON_BROWSER__ ? undefined : typeof process === "undefined" ? undefined : process.cwd());
|
|
23722
24256
|
const headers = capability ? { "x-oddington-run": capability } : {};
|
|
23723
24257
|
const request = options.request ?? (capability ? async (path, init2) => {
|
|
23724
24258
|
const origin = oddingtonOrigin();
|
|
@@ -23783,22 +24317,25 @@ function routinePublicationClient(options = {}) {
|
|
|
23783
24317
|
return validateInboxFile(await local("files/add", input));
|
|
23784
24318
|
if (!workingRoot)
|
|
23785
24319
|
throw new Error("Inbox file reads require a working root");
|
|
23786
|
-
|
|
23787
|
-
|
|
23788
|
-
|
|
23789
|
-
|
|
23790
|
-
|
|
23791
|
-
|
|
23792
|
-
|
|
23793
|
-
|
|
23794
|
-
|
|
23795
|
-
|
|
23796
|
-
|
|
23797
|
-
|
|
23798
|
-
|
|
23799
|
-
|
|
23800
|
-
|
|
23801
|
-
|
|
24320
|
+
if (typeof __ODDINGTON_BROWSER__ === "undefined" || !__ODDINGTON_BROWSER__) {
|
|
24321
|
+
const { readInboxPublicationFile: readInboxPublicationFile2 } = await Promise.resolve().then(() => (init_inbox_publication_file(), exports_inbox_publication_file));
|
|
24322
|
+
const bytes = await readInboxPublicationFile2(workingRoot, input);
|
|
24323
|
+
const name = input.name ?? input.path.split("/").at(-1);
|
|
24324
|
+
const response = await request(`/api/cli/workspaces/${workspace}/inbox/files/upload`, {
|
|
24325
|
+
method: "POST",
|
|
24326
|
+
headers: {
|
|
24327
|
+
"content-type": "application/octet-stream",
|
|
24328
|
+
...headers,
|
|
24329
|
+
"x-oddington-file": JSON.stringify({ name, ...input.csv ? { csv: input.csv } : {} })
|
|
24330
|
+
},
|
|
24331
|
+
body: Uint8Array.from(bytes).buffer
|
|
24332
|
+
});
|
|
24333
|
+
const result2 = await response.json();
|
|
24334
|
+
if (!response.ok)
|
|
24335
|
+
throw new Error(result2.error ?? "Could not retain file");
|
|
24336
|
+
return await verifyInboxFile(bytes, validateInboxFile(result2));
|
|
24337
|
+
}
|
|
24338
|
+
throw new Error("Inbox file reads require a Node working root");
|
|
23802
24339
|
}
|
|
23803
24340
|
},
|
|
23804
24341
|
async check(body2) {
|