mongoku
Version:
[](https://github.com/huggingface/Mongoku/actions/workflows/ci.yml)
1,503 lines (1,494 loc) • 46.5 kB
JavaScript
import { T as get_request_store, w as with_request_store } from './root-otUAnOAR.js';
import { p as private_env } from './shared-server-BmU87nph.js';
import './utils-BQzn9ikS.js';
import { M as MUTATIVE_METHODS, y as stringify_remote_arg, n as noop, c as create_remote_key, z as unfriendly_hydratable, q as stringify, e as handle_error_and_jsonify } from './shared-DlqhoNLb.js';
import { e as error, H as HttpError, S as SvelteKitError } from './index-NcxaM188.js';
import { p as parseJSON, v as validateAggregationPipeline, i as isEmptyObject } from './jsonParser-C3QUcODD.js';
import { J as JsonEncoder } from './JsonEncoder-Dgtqxb_U.js';
import { l as logger } from './logger-PfH_grbh.js';
import { g as getMongo } from './mongo-B92d7zNj.js';
import { a as auditSchemaCompliance } from './schema-BZonjzNJ.js';
import { ObjectId, ReadPreference } from 'mongodb';
import { z } from 'zod';
/** @import { RemoteInternals } from 'types' */
/** @type {RemoteInternals['type'][]} */
const types = ['command', 'form', 'prerender', 'query', 'query_batch', 'query_live'];
/**
* @param {Record<string, any>} module
* @param {string} file
* @param {string} hash
*/
function init_remote_functions(module, file, hash) {
if (module.default) {
throw new Error(
`Cannot export \`default\` from a remote module (${file}) — please use named exports instead`
);
}
for (const [name, fn] of Object.entries(module)) {
if (!types.includes(fn?.__?.type)) {
throw new Error(
`\`${name}\` exported from ${file} is invalid — all exports from this file must be remote functions`
);
}
fn.__.id = `${hash}/${name}`;
fn.__.name = name;
}
}
function create_validator(validate_or_fn, maybe_fn) {
if (!maybe_fn) {
return (arg) => {
if (arg !== void 0) {
error(400, "Bad Request");
}
};
}
if (validate_or_fn === "unchecked") {
return (arg) => arg;
}
if ("~standard" in validate_or_fn) {
return async (arg) => {
const { event, state } = get_request_store();
const result = await validate_or_fn["~standard"].validate(arg);
if (result.issues) {
error(
400,
await state.handleValidationError({
issues: result.issues,
event
})
);
}
return result.value;
};
}
throw new Error(
'Invalid validator passed to remote function. Expected "unchecked" or a Standard Schema (https://standardschema.dev)'
);
}
async function get_response(internals, payload, state, get_result) {
await 0;
const cache = get_cache(internals, state);
const entry = cache[payload] ??= {
serialize: false,
data: get_result()
};
entry.serialize ||= !!state.is_in_universal_load;
if (state.is_in_render && internals.id) {
const remote_key = create_remote_key(internals.id, payload);
Promise.resolve(entry.data).then((value) => {
void unfriendly_hydratable(remote_key, () => stringify(value, state.transport));
}).catch(noop);
}
return entry.data;
}
function derive_remote_function_event(event, state, allow_cookies) {
return {
event: {
...event,
setHeaders: () => {
throw new Error("setHeaders is not allowed in remote functions");
},
cookies: {
...event.cookies,
set: (name, value, opts) => {
if (!allow_cookies) {
throw new Error("Cannot set cookies in `query` or `prerender` functions");
}
if (opts.path && !opts.path.startsWith("/")) {
throw new Error("Cookies set in remote functions must have an absolute path");
}
return event.cookies.set(name, value, opts);
},
delete: (name, opts) => {
if (!allow_cookies) {
throw new Error("Cannot delete cookies in `query` or `prerender` functions");
}
if (opts.path && !opts.path.startsWith("/")) {
throw new Error("Cookies deleted in remote functions must have an absolute path");
}
return event.cookies.delete(name, opts);
}
}
},
state: {
...state,
is_in_remote_function: true
}
};
}
async function run_remote_function(event, state, allow_cookies, get_input, fn) {
const store = derive_remote_function_event(event, state, allow_cookies);
const input = await with_request_store(store, get_input);
return with_request_store(store, () => fn(input));
}
async function* run_remote_generator(event, state, allow_cookies, get_input, fn, name) {
const store = derive_remote_function_event(event, state, allow_cookies);
const input = await with_request_store(store, get_input);
const source = await with_request_store(store, () => fn(input));
const iterator = to_iterator(source, name);
let done = false;
try {
while (true) {
const result = await with_request_store(store, () => iterator.next());
if (result.done) {
done = true;
return result.value;
}
yield result.value;
}
} finally {
if (!done && typeof iterator.return === "function") {
await with_request_store(store, () => iterator.return?.(void 0));
}
}
}
function to_iterator(source, name) {
if ("next" in source && typeof source.next === "function") {
return source;
}
if (Symbol.asyncIterator in source && typeof source[Symbol.asyncIterator] === "function") {
return source[Symbol.asyncIterator]();
}
if (Symbol.iterator in source && typeof source[Symbol.iterator] === "function") {
return source[Symbol.iterator]();
}
throw new Error(
`query.live '${name}' must return an Iterator, Iterable, AsyncIterator or AsyncIterable`
);
}
function get_cache(internals, state = get_request_store().state) {
let cache = state.remote.data?.get(internals);
if (cache === void 0) {
cache = {};
(state.remote.data ??= /* @__PURE__ */ new Map()).set(internals, cache);
}
return cache;
}
// @__NO_SIDE_EFFECTS__
function command(validate_or_fn, maybe_fn) {
const fn = maybe_fn ?? validate_or_fn;
const validate = create_validator(validate_or_fn, maybe_fn);
const __ = { type: "command", id: "", name: "" };
const wrapper = (arg) => {
const { event, state } = get_request_store();
if (!MUTATIVE_METHODS.includes(event.request.method)) {
throw new Error(
`Cannot call a command (\`${__.name}(${maybe_fn ? "..." : ""})\`) from a ${event.request.method} handler`
);
}
if (state.is_in_render) {
throw new Error(
`Cannot call a command (\`${__.name}(${maybe_fn ? "..." : ""})\`) during server-side rendering`
);
}
state.remote.refreshes ??= /* @__PURE__ */ new Map();
state.remote.reconnects ??= /* @__PURE__ */ new Map();
const promise = Promise.resolve(
run_remote_function(event, state, true, () => validate(arg), fn)
);
promise.updates = () => {
throw new Error(`Cannot call '${__.name}(...).updates(...)' on the server`);
};
return (
/** @type {ReturnType<RemoteCommand<Input, Output>>} */
promise
);
};
Object.defineProperty(wrapper, "__", { value: __ });
Object.defineProperty(wrapper, "pending", {
get: () => 0
});
return wrapper;
}
// @__NO_SIDE_EFFECTS__
function query(validate_or_fn, maybe_fn) {
const fn = maybe_fn ?? validate_or_fn;
const validate = create_validator(validate_or_fn, maybe_fn);
const __ = {
type: "query",
id: "",
name: "",
validate,
bind(payload, validated_arg) {
const { event, state } = get_request_store();
return create_query_resource(
__,
payload,
state,
() => run_remote_function(event, state, false, () => validated_arg, fn)
);
}
};
const wrapper = (arg) => {
const { event, state } = get_request_store();
const payload = stringify_remote_arg(arg, state.transport);
return create_query_resource(
__,
payload,
state,
() => run_remote_function(event, state, false, () => validate(arg), fn)
);
};
Object.defineProperty(wrapper, "__", { value: __ });
return wrapper;
}
// @__NO_SIDE_EFFECTS__
function live(validate_or_fn, maybe_fn) {
const fn = maybe_fn ?? validate_or_fn;
const validate = create_validator(validate_or_fn, maybe_fn);
const run = (event, state, get_input) => run_remote_generator(event, state, false, get_input, fn, __.name);
const first_value = async (generator) => {
try {
const { value, done } = await generator.next();
if (done) {
throw new Error(`query.live '${__.name}' did not yield a value`);
}
return value;
} finally {
await generator.return(void 0);
}
};
const __ = {
type: "query_live",
id: "",
name: "",
run: (event, state, arg) => run(event, state, () => validate(arg)),
validate,
bind(payload, validated_arg) {
const { event, state } = get_request_store();
return create_live_query_resource(
__,
payload,
state,
() => first_value(run(event, state, () => validated_arg))
);
}
};
const wrapper = (arg) => {
const { event, state } = get_request_store();
const payload = stringify_remote_arg(arg, state.transport);
return create_live_query_resource(
__,
payload,
state,
() => first_value(run(event, state, () => validate(arg)))
);
};
Object.defineProperty(wrapper, "__", { value: __ });
return wrapper;
}
// @__NO_SIDE_EFFECTS__
function batch(validate_or_fn, maybe_fn) {
const fn = maybe_fn ?? validate_or_fn;
const validate = create_validator(validate_or_fn, maybe_fn);
const enqueue = (payload, get_validated) => {
const { event, state } = get_request_store();
return new Promise((resolve, reject) => {
const batches = state.remote.batches ??= /** @type {NonNullable<typeof state.remote.batches>} */
/* @__PURE__ */ new Map();
let batched = batches.get(__.id);
if (!batched) {
batched = /* @__PURE__ */ new Map();
batches.set(__.id, batched);
}
const entry = batched.get(payload);
if (entry) {
entry.resolvers.push({ resolve, reject });
return;
}
batched.set(payload, {
get_validated,
resolvers: [{ resolve, reject }]
});
if (batched.size > 1) return;
setTimeout(async () => {
batches.delete(__.id);
const entries = Array.from(batched.values());
try {
return await run_remote_function(
event,
state,
false,
async () => Promise.all(entries.map((entry2) => entry2.get_validated())),
async (input) => {
const get_result = await fn(input);
for (let i = 0; i < entries.length; i++) {
try {
const result = get_result(input[i], i);
for (const resolver of entries[i].resolvers) {
resolver.resolve(result);
}
} catch (error2) {
for (const resolver of entries[i].resolvers) {
resolver.reject(error2);
}
}
}
}
);
} catch (error2) {
for (const entry2 of batched.values()) {
for (const resolver of entry2.resolvers) {
resolver.reject(error2);
}
}
}
}, 0);
});
};
const __ = {
type: "query_batch",
id: "",
name: "",
validate,
run: async (args, options) => {
const { event, state } = get_request_store();
return run_remote_function(
event,
state,
false,
async () => Promise.all(args.map(validate)),
async (input) => {
const get_result = await fn(input);
return Promise.all(
input.map(async (arg, i) => {
try {
const data = get_result(arg, i);
return { type: "result", data: stringify(data, state.transport) };
} catch (error2) {
return {
type: "error",
error: await handle_error_and_jsonify(event, state, options, error2),
status: error2 instanceof HttpError || error2 instanceof SvelteKitError ? error2.status : 500
};
}
})
);
}
);
},
bind(payload, validated_arg) {
const { state } = get_request_store();
return create_query_resource(__, payload, state, () => enqueue(payload, () => validated_arg));
}
};
const wrapper = (arg) => {
const { state } = get_request_store();
const payload = stringify_remote_arg(arg, state.transport);
return create_query_resource(
__,
payload,
state,
() => (
// Collect all the calls to the same query in the same macrotask,
// then execute them as one backend request.
enqueue(payload, () => validate(arg))
)
);
};
Object.defineProperty(wrapper, "__", { value: __ });
return wrapper;
}
function create_query_resource(__, payload, state, fn) {
let promise = null;
const get_promise = () => {
return promise ??= get_response(__, payload, state, fn);
};
const populate_hydratable = () => {
void (__.id && state.is_in_render && get_promise());
};
return {
/** @type {Promise<any>['catch']} */
catch(onrejected) {
return get_promise().catch(onrejected);
},
get current() {
populate_hydratable();
return void 0;
},
get error() {
populate_hydratable();
return void 0;
},
/** @type {Promise<any>['finally']} */
finally(onfinally) {
return get_promise().finally(onfinally);
},
get loading() {
populate_hydratable();
return true;
},
get ready() {
populate_hydratable();
return false;
},
refresh() {
const { event } = get_request_store();
if (!event.isRemoteRequest) {
return Promise.resolve();
}
const refresh_context = get_refresh_context(__, "refresh", payload);
const is_immediate_refresh = !refresh_context.cache[refresh_context.payload];
const value = is_immediate_refresh ? get_promise() : fn();
return update_refresh_value(refresh_context, value, is_immediate_refresh);
},
run() {
if (!state.is_in_universal_load) {
throw new Error(
"On the server, .run() can only be called in universal `load` functions. Anywhere else, just await the query directly"
);
}
return get_response(__, payload, state, fn);
},
/** @param {any} value */
set(value) {
return update_refresh_value(get_refresh_context(__, "set", payload), value);
},
/** @type {Promise<any>['then']} */
then(onfulfilled, onrejected) {
return get_promise().then(onfulfilled, onrejected);
},
withOverride() {
throw new Error(`Cannot call '${__.name}.withOverride()' on the server`);
},
get [Symbol.toStringTag]() {
return "QueryResource";
}
};
}
function create_live_query_resource(__, payload, state, get_first_value) {
let promise = null;
const get_promise = () => {
return promise ??= get_response(__, payload, state, get_first_value);
};
const populate_hydratable = () => {
void (__.id && state.is_in_render && get_promise());
};
return {
/** @type {Promise<any>['catch']} */
catch(onrejected) {
return get_promise().catch(onrejected);
},
get current() {
populate_hydratable();
return void 0;
},
get error() {
populate_hydratable();
return void 0;
},
/** @type {Promise<any>['finally']} */
finally(onfinally) {
return get_promise().finally(onfinally);
},
get done() {
populate_hydratable();
return false;
},
get loading() {
populate_hydratable();
return true;
},
get ready() {
populate_hydratable();
return false;
},
get connected() {
populate_hydratable();
return false;
},
reconnect() {
const reconnects = state.remote.reconnects;
if (!reconnects) {
throw new Error(
`Cannot call reconnect on query.live '${__.name}' because it is not executed in the context of a command/form remote function`
);
}
reconnects.set(create_remote_key(__.id, payload), get_promise());
return Promise.resolve();
},
run() {
throw new Error("Cannot call .run() on a live query on the server");
},
/** @type {Promise<any>['then']} */
then(onfulfilled, onrejected) {
return get_promise().then(onfulfilled, onrejected);
},
get [Symbol.toStringTag]() {
return "LiveQueryResource";
}
};
}
Object.defineProperty(query, "batch", { value: batch, enumerable: true });
Object.defineProperty(query, "live", { value: live, enumerable: true });
function get_refresh_context(__, action, payload) {
const { state } = get_request_store();
const { refreshes } = state.remote;
if (!refreshes) {
const name = __.type === "query_batch" ? `query.batch '${__.name}'` : `query '${__.name}'`;
throw new Error(
`Cannot call ${action} on ${name} because it is not executed in the context of a command/form remote function`
);
}
const cache = get_cache(__, state);
const refreshes_key = create_remote_key(__.id, payload);
return { __, state, refreshes, refreshes_key, cache, payload };
}
function update_refresh_value({ __, refreshes, refreshes_key, cache, payload }, value, is_immediate_refresh = false) {
const promise = Promise.resolve(value);
if (!is_immediate_refresh) {
cache[payload] = { serialize: true, data: promise };
}
if (__.id) {
refreshes.set(refreshes_key, promise);
}
promise.catch(noop);
return Promise.resolve();
}
const m = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
__proto__: null,
get addServer() {
return addServer;
},
get auditSchema() {
return auditSchema;
},
get countDocuments() {
return countDocuments;
},
get countDocumentsByTimeRange() {
return countDocumentsByTimeRange;
},
get createIndex() {
return createIndex;
},
get deleteDocument() {
return deleteDocument;
},
get deleteMany() {
return deleteMany;
},
get detectPrimaryNode() {
return detectPrimaryNode;
},
get dropCollection() {
return dropCollection;
},
get dropDatabase() {
return dropDatabase;
},
get dropIndex() {
return dropIndex;
},
get explainQuery() {
return explainQuery;
},
get fetchMappedDocument() {
return fetchMappedDocument;
},
get getIndexStatsFromNodes() {
return getIndexStatsFromNodes;
},
get getIndexStatsWithReadPreference() {
return getIndexStatsWithReadPreference;
},
get getServerNodes() {
return getServerNodes;
},
get hideIndex() {
return hideIndex;
},
get insertDocument() {
return insertDocument;
},
get loadDocuments() {
return loadDocuments;
},
get removeServer() {
return removeServer;
},
get retryConnection() {
return retryConnection;
},
get unhideIndex() {
return unhideIndex;
},
get updateDocument() {
return updateDocument;
},
get updateMany() {
return updateMany;
}
}, Symbol.toStringTag, { value: "Module" }));
function checkReadOnly() {
if (private_env.MONGOKU_READ_ONLY_MODE === "true") {
error(403, "Read-only mode is enabled");
}
}
function sanitizeMongoUrl(url) {
try {
const urlObj = new URL(url.startsWith("mongodb") ? url : `mongodb://${url}`);
if (urlObj.username || urlObj.password) {
urlObj.username = "***";
urlObj.password = "***";
}
return urlObj.toString();
} catch {
return "***";
}
}
const addServer = command(
z.object({
url: z.string()
}),
async ({ url }) => {
logger.log("addServer called with payload:", { url: sanitizeMongoUrl(url) });
const mongo = await getMongo();
await mongo.addServer(url);
return { ok: true };
}
);
const removeServer = command(z.string(), async (serverName) => {
logger.log("removeServer called with payload:", { serverName });
const mongo = await getMongo();
await mongo.removeServer(serverName);
return { ok: true };
});
const updateDocument = command(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
document: z.string(),
value: z.unknown(),
partial: z.boolean().optional().default(false),
upsert: z.boolean().optional().default(false)
}),
async ({ server, database, collection, document, value, partial, upsert }) => {
logger.log("updateDocument called with payload:", { server, database, collection, document, partial, upsert });
checkReadOnly();
const mongo = await getMongo();
const client = mongo.getClient(server);
const coll = client.db(database).collection(collection);
const newValue = JsonEncoder.decode(value);
const _id = /^[0-9a-fA-F]{24}$/.test(document) ? new ObjectId(document) : document;
if (partial) {
await coll.updateOne(
{
_id
},
{ $set: newValue },
{ upsert }
);
} else {
await coll.replaceOne(
{
_id
},
JsonEncoder.decode(newValue),
{ upsert }
);
}
if (collection === "mongoku.mappings") {
client.clearMappingsCache(database, document);
}
return {
ok: true,
update: JsonEncoder.encode(newValue)
};
}
);
const insertDocument = command(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
document: z.string().nullable().optional(),
value: z.unknown()
}),
async ({ server, database, collection, document, value }) => {
logger.log("insertDocument called with payload:", { server, database, collection, document });
checkReadOnly();
const mongo = await getMongo();
const client = mongo.getClient(server);
const coll = client.db(database).collection(collection);
const newValue = JsonEncoder.decode(value);
if (document !== null && document !== void 0) {
const _id = /^[0-9a-fA-F]{24}$/.test(document) ? new ObjectId(document) : document;
newValue._id = _id;
}
const res = await coll.insertOne(newValue);
if (collection === "mongoku.mappings" && typeof res.insertedId === "string") {
client.clearMappingsCache(database, res.insertedId);
}
return {
ok: true,
insert: JsonEncoder.encode(newValue)
};
}
);
const deleteDocument = command(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
document: z.string()
}),
async ({ server, database, collection, document }) => {
logger.log("deleteDocument called with payload:", { server, database, collection, document });
checkReadOnly();
const mongo = await getMongo();
const client = mongo.getClient(server);
const _id = /^[0-9a-fA-F]{24}$/.test(document) ? new ObjectId(document) : document;
await client.db(database).collection(collection).deleteOne({
_id
});
if (collection === "mongoku.mappings") {
client.clearMappingsCache(database, document);
}
return {
ok: true
};
}
);
const updateMany = command(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
filter: z.string(),
update: z.string()
}),
async ({ server, database, collection, filter, update }) => {
logger.log("updateMany called with payload:", { server, database, collection, filter, update });
checkReadOnly();
const mongo = await getMongo();
const client = mongo.getClient(server);
const coll = client.db(database).collection(collection);
const filterDoc = JsonEncoder.decode(parseJSON(filter));
const updateDoc = JsonEncoder.decode(parseJSON(update, { allowArray: true }));
const result = await coll.updateMany(filterDoc, updateDoc);
return {
ok: true,
matchedCount: result.matchedCount,
modifiedCount: result.modifiedCount
};
}
);
const countDocuments = command(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
filter: z.string()
}),
async ({ server, database, collection, filter }) => {
const mongo = await getMongo();
const client = mongo.getClient(server);
const coll = client.db(database).collection(collection);
const filterDoc = JsonEncoder.decode(parseJSON(filter));
try {
const count = await coll.countDocuments(filterDoc, {
maxTimeMS: mongo.getCountTimeout()
});
return {
data: count,
error: null
};
} catch (err) {
logger.error("Error counting documents:", err);
return {
data: 0,
error: `Failed to count documents: ${err instanceof Error ? err.message : String(err)}`
};
}
}
);
const deleteMany = command(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
filter: z.string()
}),
async ({ server, database, collection, filter }) => {
logger.log("deleteMany called with payload:", { server, database, collection, filter });
checkReadOnly();
const mongo = await getMongo();
const client = mongo.getClient(server);
const coll = client.db(database).collection(collection);
const filterDoc = JsonEncoder.decode(parseJSON(filter));
const result = await coll.deleteMany(filterDoc);
return {
ok: true,
deletedCount: result.deletedCount
};
}
);
const hideIndex = command(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
index: z.string()
}),
async ({ server, database, collection, index }) => {
logger.log("hideIndex called with payload:", { server, database, collection, index });
checkReadOnly();
const mongo = await getMongo();
const client = mongo.getClient(server);
await client.db(database).command({
collMod: collection,
index: {
name: index,
hidden: true
}
});
return {
ok: true
};
}
);
const unhideIndex = command(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
index: z.string()
}),
async ({ server, database, collection, index }) => {
logger.log("unhideIndex called with payload:", { server, database, collection, index });
checkReadOnly();
const mongo = await getMongo();
const client = mongo.getClient(server);
await client.db(database).command({
collMod: collection,
index: {
name: index,
hidden: false
}
});
return {
ok: true
};
}
);
const createIndex = command(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
keys: z.string(),
name: z.string().optional(),
unique: z.boolean().optional(),
sparse: z.boolean().optional(),
partialFilterExpression: z.string().optional(),
expireAfterSeconds: z.number().optional(),
background: z.boolean().optional()
}),
async ({
server,
database,
collection,
keys,
name,
unique,
sparse,
partialFilterExpression,
expireAfterSeconds,
background
}) => {
logger.log("createIndex called with payload:", { server, database, collection, keys, name, unique, sparse });
checkReadOnly();
const mongo = await getMongo();
const client = mongo.getClient(server);
const coll = client.db(database).collection(collection);
const keysDoc = JsonEncoder.decode(parseJSON(keys));
const options = {};
if (name) {
options.name = name;
}
if (unique) {
options.unique = unique;
}
if (sparse) {
options.sparse = sparse;
}
if (partialFilterExpression) {
options.partialFilterExpression = JsonEncoder.decode(parseJSON(partialFilterExpression));
}
if (expireAfterSeconds !== void 0) {
options.expireAfterSeconds = expireAfterSeconds;
}
if (background) {
options.background = background;
}
await coll.createIndex(keysDoc, options);
return {
ok: true
};
}
);
const dropIndex = command(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
index: z.string()
}),
async ({ server, database, collection, index }) => {
logger.log("dropIndex called with payload:", { server, database, collection, index });
checkReadOnly();
const mongo = await getMongo();
const client = mongo.getClient(server);
await client.db(database).command({
dropIndexes: collection,
index
});
return {
ok: true
};
}
);
const dropCollection = command(
z.object({
server: z.string(),
database: z.string(),
collection: z.string()
}),
async ({ server, database, collection }) => {
logger.log("dropCollection called with payload:", { server, database, collection });
checkReadOnly();
const mongo = await getMongo();
const client = mongo.getClient(server);
const db = client.db(database);
await db.dropCollection(collection);
return {
ok: true
};
}
);
const dropDatabase = command(
z.object({
server: z.string(),
database: z.string()
}),
async ({ server, database }) => {
logger.log("dropDatabase called with payload:", { server, database });
checkReadOnly();
const mongo = await getMongo();
const client = mongo.getClient(server);
await client.db(database).dropDatabase();
return {
ok: true
};
}
);
const retryConnection = command(z.string(), async (serverId) => {
logger.log("retryConnection called with payload:", { serverId });
const mongo = await getMongo();
await mongo.reconnectClient(serverId);
return { ok: true };
});
const loadDocuments = command(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
query: z.string().default("{}"),
sort: z.string().default("{}"),
project: z.string().default("{}"),
skip: z.number().int().default(0),
limit: z.number().int().default(20),
mode: z.enum(["query", "distinct", "aggregation"]).default("query"),
field: z.string().optional()
}),
async ({ server, database, collection, query: queryStr, sort, project, skip, limit, mode, field }) => {
let queryDoc;
try {
queryDoc = parseJSON(queryStr, { allowArray: true });
} catch (err) {
error(400, `Invalid query: ${err}`);
}
try {
parseJSON(sort);
} catch (err) {
error(400, `Invalid sort: ${err}`);
}
try {
parseJSON(project);
} catch (err) {
error(400, `Invalid project: ${err}`);
}
const sortDoc = parseJSON(sort);
const projectDoc = parseJSON(project);
const mongo = await getMongo();
const client = mongo.getClient(server);
const coll = client.db(database).collection(collection);
if (mode === "distinct") {
if (!field) {
error(400, "Invalid distinct query: field name is required");
}
try {
const results = await coll.distinct(field, JsonEncoder.decode(queryDoc), {
maxTimeMS: mongo.getQueryTimeout()
});
return {
data: results.map((value) => JsonEncoder.encode({ value })),
error: null,
isAggregation: false,
isDistinct: true
};
} catch (err) {
logger.error("Error executing distinct:", err);
error(500, `Failed to execute distinct: ${err instanceof Error ? err.message : String(err)}`);
}
}
if (mode === "aggregation" && Array.isArray(queryDoc)) {
try {
validateAggregationPipeline(queryDoc);
} catch (err) {
error(400, `Invalid aggregation pipeline: ${err instanceof Error ? err.message : String(err)}`);
}
const pipeline = JsonEncoder.decode(queryDoc);
try {
const results = await coll.aggregate(
[
...pipeline,
...isEmptyObject(projectDoc) ? [] : [{ $project: projectDoc }],
...isEmptyObject(sortDoc) ? [] : [{ $sort: sortDoc }],
{ $limit: limit },
{ $skip: skip }
],
{
maxTimeMS: mongo.getQueryTimeout()
}
).map((obj) => JsonEncoder.encode(obj)).toArray();
return {
data: results,
error: null,
isAggregation: true,
isDistinct: false
};
} catch (err) {
logger.error("Error executing aggregation:", err);
error(500, `Failed to execute aggregation: ${err instanceof Error ? err.message : String(err)}`);
}
}
try {
const results = await coll.find(JsonEncoder.decode(queryDoc), { maxTimeMS: mongo.getQueryTimeout() }).project(projectDoc).sort(JsonEncoder.decode(sortDoc)).limit(limit).skip(skip).map((obj) => JsonEncoder.encode(obj)).toArray();
return {
data: results,
error: null,
isAggregation: false,
isDistinct: false
};
} catch (err) {
logger.error("Error fetching query results:", err);
error(500, `Failed to fetch query results: ${err instanceof Error ? err.message : String(err)}`);
}
}
);
const fetchMappedDocument = query(
z.object({
server: z.string(),
database: z.string(),
mappings: z.array(
z.union([
z.object({
type: z.literal("document"),
collection: z.string(),
on: z.string()
}),
z.object({
type: z.literal("url"),
template: z.string()
}),
// Legacy format (backwards compatibility)
z.object({
collection: z.string(),
on: z.string()
})
])
),
value: z.unknown()
}),
async ({ server, database, mappings, value }) => {
const mongo = await getMongo();
const client = mongo.getClient(server);
const decodedValue = JsonEncoder.decode(value);
const documentMappings = mappings.filter((m2) => {
if ("type" in m2) {
return m2.type === "document";
}
return "collection" in m2 && "on" in m2;
});
for (const mapping of documentMappings) {
try {
if ("type" in mapping && mapping.type !== "document") {
continue;
}
const collection = "collection" in mapping ? mapping.collection : "";
const on = "on" in mapping ? mapping.on : "_id";
const coll = client.db(database).collection(collection);
const query2 = { [on]: decodedValue };
const document = await coll.findOne(query2, { maxTimeMS: mongo.getQueryTimeout() });
if (document) {
return {
data: JsonEncoder.encode(document),
collection,
error: null
};
}
} catch (err) {
const collection = "collection" in mapping ? mapping.collection : "unknown";
const on = "on" in mapping ? mapping.on : "_id";
logger.error(`Error fetching mapped document from ${collection}.${on}:`, err);
continue;
}
}
return {
data: null,
collection: null,
error: "Document not found in any mapped collection"
};
}
);
const getIndexStatsWithReadPreference = query(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
readPreferenceMode: z.string().optional(),
readPreferenceTags: z.string().optional()
}),
async ({ server, database, collection, readPreferenceMode, readPreferenceTags }) => {
const mongo = await getMongo();
const client = mongo.getClient(server);
try {
let tags;
if (readPreferenceTags) {
try {
tags = parseJSON(readPreferenceTags);
if (typeof tags !== "object" || tags === null) {
error(400, "Invalid read preference tags format");
}
if (!Array.isArray(tags)) {
tags = [tags];
}
} catch (err) {
error(400, `Invalid read preference tags JSON: ${err}`);
}
}
let readPreference;
if (readPreferenceMode || tags) {
const mode = readPreferenceMode || "nearest";
readPreference = tags ? new ReadPreference(mode, tags) : new ReadPreference(mode);
}
const coll = client.db(database).collection(collection);
const aggregateOptions = readPreference ? { readPreference } : {};
const statsResult = await coll.aggregate([{ $indexStats: {} }], aggregateOptions).toArray();
const indexStats = Object.fromEntries(
statsResult.map((stat) => [
stat.name,
{
ops: stat.accesses?.ops || 0,
since: stat.accesses?.since || /* @__PURE__ */ new Date(),
host: stat.host || "unknown"
}
])
);
return {
data: JsonEncoder.encode(indexStats),
error: null
};
} catch (err) {
logger.error("Error fetching index stats with read preference:", err);
return {
data: {},
error: `Failed to fetch index stats: ${err instanceof Error ? err.message : String(err)}`
};
}
}
);
const getServerNodes = query(
z.object({
server: z.string()
}),
async ({ server }) => {
const mongo = await getMongo();
try {
const nodes = await mongo.getServerNodes(server);
return {
data: nodes,
error: null
};
} catch (err) {
logger.error("Error getting server nodes:", err);
return {
data: [],
error: `Failed to get server nodes: ${err instanceof Error ? err.message : String(err)}`
};
}
}
);
const detectPrimaryNode = query(
z.object({
server: z.string(),
database: z.string(),
collection: z.string()
}),
async ({ server, database, collection }) => {
const mongo = await getMongo();
const client = mongo.getClient(server);
try {
const coll = client.db(database).collection(collection);
const statsResult = await coll.aggregate([{ $indexStats: {} }], { readPreference: new ReadPreference("primary") }).toArray();
const primaryHost = statsResult.length > 0 ? statsResult[0].host : null;
return {
data: primaryHost,
error: null
};
} catch (err) {
logger.error("Error detecting primary node:", err);
return {
data: null,
error: `Failed to detect primary: ${err instanceof Error ? err.message : String(err)}`
};
}
}
);
const getIndexStatsFromNodes = query(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
nodes: z.array(z.string())
}),
async ({ server, database, collection, nodes }) => {
const mongo = await getMongo();
try {
const results = await Promise.allSettled(
nodes.map(async (node) => {
const stats = await mongo.getIndexStatsFromNode(server, node, database, collection);
return { node, stats };
})
);
const mergedStats = {};
const errors = [];
let i = 0;
for (const result of results) {
if (result.status === "fulfilled") {
const { stats } = result.value;
for (const [indexName, indexStats] of Object.entries(stats)) {
const key = `${indexName}::${indexStats.host}`;
mergedStats[key] = indexStats;
}
} else {
logger.error(`Error fetching index stats from node ${nodes[i]}:`, result.reason);
errors.push(result.reason?.message || String(result.reason));
}
i++;
}
return {
data: JsonEncoder.encode(mergedStats),
error: errors.length > 0 ? errors.join("; ") : null
};
} catch (err) {
logger.error("Error fetching index stats from nodes:", err);
return {
data: {},
error: `Failed to fetch index stats: ${err instanceof Error ? err.message : String(err)}`
};
}
}
);
const explainQuery = query(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
query: z.string().default("{}"),
sort: z.string().default("{}"),
project: z.string().default("{}"),
skip: z.number().int().default(0),
limit: z.number().int().default(20),
mode: z.enum(["query", "aggregation"]).default("query"),
verbosity: z.enum(["queryPlanner", "executionStats", "allPlansExecution"]).default("executionStats")
}),
async ({ server, database, collection, query: queryStr, sort, project, skip, limit, mode, verbosity }) => {
const queryDoc = (() => {
try {
return parseJSON(queryStr, { allowArray: true });
} catch (err) {
error(400, `Invalid query: ${err}`);
}
})();
try {
parseJSON(sort);
} catch (err) {
error(400, `Invalid sort: ${err}`);
}
try {
parseJSON(project);
} catch (err) {
error(400, `Invalid project: ${err}`);
}
const sortDoc = parseJSON(sort);
const projectDoc = parseJSON(project);
const mongo = await getMongo();
const client = mongo.getClient(server);
const coll = client.db(database).collection(collection);
try {
let explainResult;
if (mode === "aggregation" && Array.isArray(queryDoc)) {
try {
validateAggregationPipeline(queryDoc);
} catch (err) {
error(400, `Invalid aggregation pipeline: ${err instanceof Error ? err.message : String(err)}`);
}
const pipeline = JsonEncoder.decode(queryDoc);
const fullPipeline = [
...pipeline,
...isEmptyObject(projectDoc) ? [] : [{ $project: projectDoc }],
...isEmptyObject(sortDoc) ? [] : [{ $sort: sortDoc }],
{ $skip: skip },
{ $limit: limit }
];
explainResult = await coll.aggregate(fullPipeline).explain(verbosity);
} else {
explainResult = await coll.find(JsonEncoder.decode(queryDoc)).project(projectDoc).sort(JsonEncoder.decode(sortDoc)).skip(skip).limit(limit).explain(verbosity);
}
const plainResult = JSON.parse(JSON.stringify(explainResult));
return {
data: plainResult,
error: null
};
} catch (err) {
logger.error("Error explaining query:", err);
return {
data: null,
error: `Failed to explain query: ${err instanceof Error ? err.message : String(err)}`
};
}
}
);
const countDocumentsByTimeRange = query(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
days: z.number(),
query: z.string().optional()
}),
async ({
server,
database,
collection,
days,
query: queryStr
}) => {
const mongo = await getMongo();
const client = mongo.getClient(server);
const coll = client.db(database).collection(collection);
let baseQuery = {};
if (queryStr) {
try {
const parsed = parseJSON(queryStr);
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
baseQuery = JsonEncoder.encode(parsed);
}
} catch {
}
}
let useCreatedAt = false;
try {
const sample = await coll.findOne({}, { projection: { _id: 1, createdAt: 1 }, maxTimeMS: 5e3 });
if (sample) {
const idIsObjectId = sample._id instanceof ObjectId;
if (!idIsObjectId) {
if (sample.createdAt instanceof Date && await client.hasIndexOnField(database, collection, "createdAt")) {
useCreatedAt = true;
} else {
return { count: null, error: "Cannot determine document age (no ObjectId or createdAt field)" };
}
}
}
} catch {
}
const dateThreshold = /* @__PURE__ */ new Date();
dateThreshold.setDate(dateThreshold.getDate() - days);
let filter;
if (useCreatedAt) {
filter = { ...baseQuery, createdAt: { $gte: dateThreshold } };
} else {
const objectIdThreshold = ObjectId.createFromTime(Math.floor(dateThreshold.getTime() / 1e3));
filter = { ...baseQuery, _id: { $gte: objectIdThreshold } };
}
try {
const count = await coll.countDocuments(filter, { maxTimeMS: mongo.getCountTimeout() });
return { count, error: null };
} catch (err) {
logger.error(`Error counting documents for ${days} days:`, err);
const errorMsg = err instanceof Error ? err.message : String(err);
return { count: null, error: errorMsg };
}
}
);
const auditSchema = command(
z.object({
server: z.string(),
database: z.string(),
collection: z.string(),
readPreferenceMode: z.string().optional(),
readPreferenceTags: z.string().optional()
}),
async ({ server, database, collection, readPreferenceMode, readPreferenceTags }) => {
const mongo = await getMongo();
const client = mongo.getClient(server);
try {
let readPreference;
if (readPreferenceMode) {
let tags;
if (readPreferenceTags) {
try {
const parsed = parseJSON(readPreferenceTags, { allowArray: true });
if (Array.isArray(parsed)) {
tags = parsed;
} else if (parsed && typeof parsed === "object") {
tags = [parsed];
}
} catch {
}
}
readPreference = tags ? new ReadPreference(
readPreferenceMode,
tags
) : new ReadPreference(
readPreferenceMode
);
}
const result = await auditSchemaCompliance(client, database, collection, {
readPreference,
maxTimeMS: mongo.getQueryTimeout()
});
return {
data: JsonEncoder.encode(result),
error: null
};
} catch (err) {
logger.error("Error auditing schema compliance:", err);
return {
data: null,
error: `Failed to audit schema: ${err instanceof Error ? err.message : String(err)}`
};
}
}
);
init_remote_functions(m, "src/api/servers.remote.ts", "xxtqbu");
for (const [name, fn] of Object.entries(m)) {
fn.__.id = "xxtqbu/" + name;
fn.__.name = name;
}
export { countDocuments as c, deleteDocument as d, explainQuery as e, fetchMappedDocument as f, loadDocuments as l, m, updateDocument as u };
//# sourceMappingURL=servers.remote-CGenkEHX.js.map