UNPKG

@mercnet/core

Version:

Core utilities and types for MercNet ecosystem

778 lines (770 loc) 23 kB
'use strict'; 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