@mercnet/core
Version:
Core utilities and types for MercNet ecosystem
778 lines (770 loc) • 23 kB
JavaScript
;
var graphqlRequest = require('graphql-request');
var gql = require('graphql-tag');
var zod = require('zod');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var gql__default = /*#__PURE__*/_interopDefault(gql);
// @mercnet/core - Built with tsup
// src/utils/index.ts
var capitalize = (str) => {
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
};
var camelCase = (str) => {
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => {
return index === 0 ? word.toLowerCase() : word.toUpperCase();
}).replace(/\s+/g, "");
};
var pick = (obj, keys) => {
const result = {};
keys.forEach((key) => {
if (key in obj) {
result[key] = obj[key];
}
});
return result;
};
var isObject = (item) => {
return item !== null && typeof item === "object" && !Array.isArray(item);
};
var isString = (value) => {
return typeof value === "string";
};
var isNumber = (value) => {
return typeof value === "number" && !isNaN(value);
};
var isBoolean = (value) => {
return typeof value === "boolean";
};
var isFunction = (value) => {
return typeof value === "function";
};
var isArray = (value) => {
return Array.isArray(value);
};
var unique = (array) => {
return Array.from(new Set(array));
};
var sleep = (ms) => {
return new Promise((resolve) => globalThis.setTimeout(resolve, ms));
};
var retry = async (fn, options) => {
let lastError;
for (let i = 0; i <= options.retries; i++) {
try {
return await fn();
} catch (error) {
lastError = error;
if (i < options.retries) {
const delay = options.delay || 1e3;
const backoff = options.backoff || 1;
await sleep(delay * Math.pow(backoff, i));
}
}
}
throw lastError;
};
var formatDate = (date, format = "YYYY-MM-DD") => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0");
return format.replace("YYYY", String(year)).replace("MM", month).replace("DD", day).replace("HH", hours).replace("mm", minutes).replace("ss", seconds);
};
var isValidDate = (date) => {
return date instanceof Date && !isNaN(date.getTime());
};
var getEnv = (key, defaultValue) => {
if (typeof process !== "undefined" && process.env) {
return process.env[key] || defaultValue || "";
}
return defaultValue || "";
};
var isDevelopment = () => {
return getEnv("NODE_ENV") === "development";
};
var isProduction = () => {
return getEnv("NODE_ENV") === "production";
};
var MercNetError = class extends Error {
constructor(message, code, details) {
super(message);
this.code = code;
this.details = details;
this.name = "MercNetError";
}
};
var createError = (message, code, details) => {
return new MercNetError(message, code, details);
};
var GetAllMarketsDocument = gql__default.default`
query GetAllMarkets {
marketCollection {
edges {
node {
id
name
short_name
ticker
price
type
created_at
last_update_at
trend_status
sentiment
sentiment_update_at
market_activity_score_1h
market_activity_score_1d
data
status
}
}
}
}
`;
var GetMarketByIdDocument = gql__default.default`
query GetMarketById($id: Int!) {
marketCollection(filter: { id: { eq: $id } }) {
edges {
node {
id
name
short_name
ticker
price
type
created_at
last_update_at
trend_status
sentiment
sentiment_update_at
market_activity_score_1h
market_activity_score_1d
data
status
}
}
}
}
`;
var GetMarketByTickerDocument = gql__default.default`
query GetMarketByTicker($ticker: String!) {
marketCollection(filter: { ticker: { eq: $ticker } }) {
edges {
node {
id
name
short_name
ticker
price
type
created_at
last_update_at
trend_status
sentiment
sentiment_update_at
market_activity_score_1h
market_activity_score_1d
data
status
}
}
}
}
`;
var GetMarketsByStatusDocument = gql__default.default`
query GetMarketsByStatus($status: String!) {
marketCollection(filter: { status: { eq: $status } }) {
edges {
node {
id
name
short_name
ticker
price
type
created_at
last_update_at
trend_status
sentiment
sentiment_update_at
market_activity_score_1h
market_activity_score_1d
data
status
}
}
}
}
`;
var defaultWrapper = (action, _operationName, _operationType, _variables) => action();
function getSdk(client, withWrapper = defaultWrapper) {
return {
GetAllMarkets(variables, requestHeaders, signal) {
return withWrapper(
(wrappedRequestHeaders) => client.request({
document: GetAllMarketsDocument,
variables,
requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders },
signal
}),
"GetAllMarkets",
"query",
variables
);
},
GetMarketById(variables, requestHeaders, signal) {
return withWrapper(
(wrappedRequestHeaders) => client.request({
document: GetMarketByIdDocument,
variables,
requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders },
signal
}),
"GetMarketById",
"query",
variables
);
},
GetMarketByTicker(variables, requestHeaders, signal) {
return withWrapper(
(wrappedRequestHeaders) => client.request({
document: GetMarketByTickerDocument,
variables,
requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders },
signal
}),
"GetMarketByTicker",
"query",
variables
);
},
GetMarketsByStatus(variables, requestHeaders, signal) {
return withWrapper(
(wrappedRequestHeaders) => client.request({
document: GetMarketsByStatusDocument,
variables,
requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders },
signal
}),
"GetMarketsByStatus",
"query",
variables
);
}
};
}
// src/graphql/index.ts
var DEFAULT_CONFIG = {
retries: 3,
debug: false
};
var MercNetGraphQLClient = class {
client;
config;
sdk;
constructor(config) {
this.config = { ...DEFAULT_CONFIG, ...config };
this.client = new graphqlRequest.GraphQLClient(this.config.endpoint, {
headers: this.config.headers
});
this.sdk = getSdk(this.client);
}
// Update headers (useful for authentication)
setHeaders(headers) {
this.client.setHeaders(headers);
}
// Add a single header
setHeader(key, value) {
this.client.setHeader(key, value);
}
// Execute a GraphQL query
async query(query, variables, requestHeaders) {
try {
if (this.config.debug) {
console.log("[GraphQL Query]", { query, variables });
}
const response = await this.client.request(query, variables, requestHeaders);
if (this.config.debug) {
console.log("[GraphQL Response]", response);
}
return response;
} catch (error) {
if (this.config.debug) {
console.error("[GraphQL Error]", error);
}
throw this.handleGraphQLError(error);
}
}
// Execute a GraphQL mutation
async mutate(mutation, variables, requestHeaders) {
return this.query(mutation, variables, requestHeaders);
}
// Execute raw request with full response
async rawRequest(query, variables, requestHeaders) {
try {
const response = await this.client.rawRequest(query, variables, requestHeaders);
return {
data: response.data,
errors: response.errors
};
} catch (error) {
throw this.handleGraphQLError(error);
}
}
// Typed methods for market operations using generated SDK
async getAllMarkets() {
try {
if (this.config.debug) {
console.log("[GraphQL] Fetching all markets");
}
const response = await this.sdk.GetAllMarkets();
const markets = this.transformMarketsResponse(response);
if (this.config.debug) {
console.log("[GraphQL] Retrieved markets:", markets.length);
}
return markets;
} catch (error) {
throw this.handleGraphQLError(error);
}
}
async getMarketById(id) {
try {
if (this.config.debug) {
console.log("[GraphQL] Fetching market by ID:", id);
}
const response = await this.sdk.GetMarketById({ id });
const markets = this.transformMarketsResponse(response);
const market = markets.length > 0 ? markets[0] : null;
if (this.config.debug) {
console.log("[GraphQL] Retrieved market:", market ? market.id : "not found");
}
return market;
} catch (error) {
throw this.handleGraphQLError(error);
}
}
async getMarketByTicker(ticker) {
try {
if (this.config.debug) {
console.log("[GraphQL] Fetching market by ticker:", ticker);
}
const response = await this.sdk.GetMarketByTicker({ ticker });
const markets = this.transformMarketsResponse(response);
const market = markets.length > 0 ? markets[0] : null;
if (this.config.debug) {
console.log("[GraphQL] Retrieved market by ticker:", market ? market.ticker : "not found");
}
return market;
} catch (error) {
throw this.handleGraphQLError(error);
}
}
async getMarketsByStatus(status) {
try {
if (this.config.debug) {
console.log("[GraphQL] Fetching markets by status:", status);
}
const response = await this.sdk.GetMarketsByStatus({ status });
const markets = this.transformMarketsResponse(response);
if (this.config.debug) {
console.log("[GraphQL] Retrieved markets by status:", markets.length);
}
return markets;
} catch (error) {
throw this.handleGraphQLError(error);
}
}
// Transform GraphQL response to Market array
transformMarketsResponse(response) {
if (!response.marketCollection?.edges) {
return [];
}
return response.marketCollection.edges.filter((edge) => edge?.node).map((edge) => {
const node = edge.node;
return {
id: node.id,
name: node.name,
short_name: node.short_name,
ticker: node.ticker,
price: node.price,
type: node.type,
created_at: node.created_at,
last_update_at: node.last_update_at,
trend_status: node.trend_status,
sentiment: node.sentiment,
sentiment_update_at: node.sentiment_update_at,
market_activity_score_1h: node.market_activity_score_1h,
market_activity_score_1d: node.market_activity_score_1d,
data: node.data,
status: node.status
};
});
}
// Handle GraphQL errors
handleGraphQLError(error) {
if (error.response?.errors) {
const graphqlErrors = error.response.errors;
const message = graphqlErrors.map((e) => e.message).join(", ");
return new MercNetError(message, "GRAPHQL_ERROR", {
errors: graphqlErrors,
query: error.request?.query,
variables: error.request?.variables
});
}
if (error.message) {
return new MercNetError(error.message, "GRAPHQL_NETWORK_ERROR", {
originalError: error
});
}
return new MercNetError("Unknown GraphQL error", "GRAPHQL_UNKNOWN_ERROR", {
originalError: error
});
}
};
var createGraphQLClient = (config) => {
return new MercNetGraphQLClient(config);
};
var COMMON_FRAGMENTS = {
// Market fragment
MARKET_FRAGMENT: `
fragment MarketFragment on market {
id
name
short_name
ticker
price
type
created_at
last_update_at
trend_status
sentiment
sentiment_update_at
market_activity_score_1h
market_activity_score_1d
data
status
}
`,
// Pagination info fragment
PAGINATION_INFO: `
fragment PaginationInfo on PageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
`,
// Error fragment
ERROR_INFO: `
fragment ErrorInfo on Error {
message
code
details
}
`
};
var QueryBuilder = class {
fields = [];
fragments = [];
variables = [];
// Add a field to the query
field(name, subFields) {
if (subFields) {
this.fields.push(`${name} { ${subFields.join(" ")} }`);
} else {
this.fields.push(name);
}
return this;
}
// Add a fragment to the query
fragment(name, fragment) {
this.fragments.push(fragment);
this.fields.push(`...${name}`);
return this;
}
// Add a variable to the query
variable(name, type) {
this.variables.push(`$${name}: ${type}`);
return this;
}
// Build the final query
build(operationType = "query", operationName) {
const variablesPart = this.variables.length > 0 ? `(${this.variables.join(", ")})` : "";
const operationPart = operationName ? `${operationType} ${operationName}${variablesPart}` : `${operationType}${variablesPart}`;
const query = `
${operationPart} {
${this.fields.join("\n")}
}
${this.fragments.join("\n")}
`;
return query.trim();
}
};
var createQueryBuilder = () => {
return new QueryBuilder();
};
var gql2 = (strings, ...values) => {
return strings.reduce((result, string, index) => {
return result + string + (values[index] || "");
}, "");
};
var getSchemaInfo = async (client) => {
const introspectionQuery = gql2`
query IntrospectionQuery {
__schema {
types {
name
kind
}
queryType {
fields {
name
}
}
mutationType {
fields {
name
}
}
subscriptionType {
fields {
name
}
}
}
}
`;
const response = await client.query(introspectionQuery);
const schema = response.__schema;
return {
types: schema.types.map((type) => type.name),
queries: schema.queryType?.fields?.map((field) => field.name) || [],
mutations: schema.mutationType?.fields?.map((field) => field.name) || [],
subscriptions: schema.subscriptionType?.fields?.map((field) => field.name) || []
};
};
var baseEntitySchema = zod.z.object({
id: zod.z.string().min(1, "ID is required"),
createdAt: zod.z.date(),
updatedAt: zod.z.date()
});
var paginationParamsSchema = zod.z.object({
page: zod.z.number().int().positive().optional().default(1),
limit: zod.z.number().int().positive().max(100).optional().default(20),
offset: zod.z.number().int().nonnegative().optional()
});
var apiResponseSchema = (dataSchema) => zod.z.object({
data: dataSchema.optional(),
error: zod.z.object({
code: zod.z.string(),
message: zod.z.string(),
details: zod.z.record(zod.z.unknown()).optional()
}).optional(),
success: zod.z.boolean(),
message: zod.z.string().optional()
});
var validators = {
email: zod.z.string().email("Invalid email format"),
password: zod.z.string().min(8, "Password must be at least 8 characters"),
url: zod.z.string().url("Invalid URL format"),
phone: zod.z.string().regex(/^\+?[1-9]\d{1,14}$/, "Invalid phone number format"),
uuid: zod.z.string().uuid("Invalid UUID format"),
slug: zod.z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Invalid slug format"),
hex: zod.z.string().regex(/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/, "Invalid hex color"),
// Date validators
dateString: zod.z.string().datetime("Invalid date format"),
dateRange: zod.z.object({
start: zod.z.date(),
end: zod.z.date()
}).refine((data) => data.end > data.start, {
message: "End date must be after start date",
path: ["end"]
}),
// Number validators
positiveNumber: zod.z.number().positive("Must be a positive number"),
nonNegativeNumber: zod.z.number().nonnegative("Must be a non-negative number"),
percentage: zod.z.number().min(0).max(100, "Must be between 0 and 100"),
currency: zod.z.number().multipleOf(0.01, "Invalid currency format"),
// String validators
nonEmptyString: zod.z.string().min(1, "Cannot be empty"),
alphanumeric: zod.z.string().regex(/^[a-zA-Z0-9]+$/, "Must contain only letters and numbers"),
noWhitespace: zod.z.string().regex(/^\S+$/, "Cannot contain whitespace")
};
var environmentSchema = zod.z.enum(["development", "production", "test", "staging"]);
var baseConfigSchema = zod.z.object({
environment: environmentSchema,
apiUrl: validators.url,
graphqlUrl: validators.url.optional(),
debug: zod.z.boolean().optional().default(false)
});
var ValidationError = class extends MercNetError {
constructor(message, issues) {
super(message, "VALIDATION_ERROR", { issues });
this.issues = issues;
this.name = "ValidationError";
}
};
var validate = (schema, data, options) => {
const result = schema.safeParse(data);
if (result.success) {
return { success: true, data: result.data };
}
const error = new ValidationError(
options?.errorMessage || "Validation failed",
result.error.issues
);
if (options?.throwOnError) {
throw error;
}
return { success: false, error };
};
var validateAndThrow = (schema, data, errorMessage) => {
const result = validate(schema, data, { throwOnError: true, errorMessage });
return result.data;
};
var createArraySchema = (itemSchema, options) => {
let schema = zod.z.array(itemSchema);
if (options?.minLength !== void 0) {
schema = schema.min(options.minLength, `Must have at least ${options.minLength} items`);
}
if (options?.maxLength !== void 0) {
schema = schema.max(options.maxLength, `Must have at most ${options.maxLength} items`);
}
if (options?.unique) {
schema = schema.refine(
(items) => {
const set = new Set(items.map((item) => JSON.stringify(item)));
return set.size === items.length;
},
{ message: "Items must be unique" }
);
}
return schema;
};
var createOptionalWithDefault = (schema, defaultValue) => {
return schema.optional().default(defaultValue);
};
var transforms = {
stringToNumber: zod.z.string().transform((val, ctx) => {
const parsed = parseFloat(val);
if (isNaN(parsed)) {
ctx.addIssue({
code: zod.z.ZodIssueCode.custom,
message: "Not a number"
});
return zod.z.NEVER;
}
return parsed;
}),
stringToBoolean: zod.z.string().transform((val) => {
return val.toLowerCase() === "true" || val === "1";
}),
stringToDate: zod.z.string().transform((val, ctx) => {
const date = new Date(val);
if (isNaN(date.getTime())) {
ctx.addIssue({
code: zod.z.ZodIssueCode.custom,
message: "Invalid date"
});
return zod.z.NEVER;
}
return date;
}),
trimString: zod.z.string().transform((val) => val.trim()),
toLowerCase: zod.z.string().transform((val) => val.toLowerCase()),
toUpperCase: zod.z.string().transform((val) => val.toUpperCase())
};
var createConditionalSchema = (baseSchema, conditions) => {
return baseSchema.superRefine((data, ctx) => {
for (const condition of conditions) {
if (condition.when(data)) {
const result = condition.then.safeParse(data);
if (!result.success) {
result.error.issues.forEach((issue) => {
ctx.addIssue({
...issue,
message: condition.message || issue.message
});
});
}
}
}
});
};
var customValidators = {
strongPassword: zod.z.string().refine(
(password) => {
const hasUpperCase = /[A-Z]/.test(password);
const hasLowerCase = /[a-z]/.test(password);
const hasNumbers = /\d/.test(password);
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password);
return hasUpperCase && hasLowerCase && hasNumbers && hasSpecialChar;
},
{
message: "Password must contain uppercase, lowercase, number and special character"
}
),
uniqueArray: (itemSchema, keyExtractor) => zod.z.array(itemSchema).refine(
(items) => {
if (!keyExtractor) {
return new Set(items).size === items.length;
}
const keys = items.map(keyExtractor);
return new Set(keys).size === keys.length;
},
{ message: "Array items must be unique" }
),
futureDate: zod.z.date().refine((date) => date > /* @__PURE__ */ new Date(), { message: "Date must be in the future" }),
pastDate: zod.z.date().refine((date) => date < /* @__PURE__ */ new Date(), { message: "Date must be in the past" })
};
// src/index.ts
var VERSION = "0.1.0";
Object.defineProperty(exports, "z", {
enumerable: true,
get: function () { return zod.z; }
});
exports.ApiResponseSchema = apiResponseSchema;
exports.BaseConfigSchema = baseConfigSchema;
exports.BaseEntitySchema = baseEntitySchema;
exports.COMMON_FRAGMENTS = COMMON_FRAGMENTS;
exports.EnvironmentSchema = environmentSchema;
exports.MercNetError = MercNetError;
exports.MercNetGraphQLClient = MercNetGraphQLClient;
exports.PaginationParamsSchema = paginationParamsSchema;
exports.QueryBuilder = QueryBuilder;
exports.VERSION = VERSION;
exports.ValidationError = ValidationError;
exports.camelCase = camelCase;
exports.capitalize = capitalize;
exports.createArraySchema = createArraySchema;
exports.createConditionalSchema = createConditionalSchema;
exports.createError = createError;
exports.createGraphQLClient = createGraphQLClient;
exports.createOptionalWithDefault = createOptionalWithDefault;
exports.createQueryBuilder = createQueryBuilder;
exports.customValidators = customValidators;
exports.formatDate = formatDate;
exports.getEnv = getEnv;
exports.getSchemaInfo = getSchemaInfo;
exports.gql = gql2;
exports.isArray = isArray;
exports.isBoolean = isBoolean;
exports.isDevelopment = isDevelopment;
exports.isFunction = isFunction;
exports.isNumber = isNumber;
exports.isObject = isObject;
exports.isProduction = isProduction;
exports.isString = isString;
exports.isValidDate = isValidDate;
exports.pick = pick;
exports.retry = retry;
exports.sleep = sleep;
exports.transforms = transforms;
exports.unique = unique;
exports.validate = validate;
exports.validateAndThrow = validateAndThrow;
exports.validators = validators;
//# sourceMappingURL=index.cjs.map
//# sourceMappingURL=index.cjs.map