nexting
Version:
A comprehensive, type-safe full-stack library for TypeScript/JavaScript applications. Provides server actions, API controllers, React hooks, error handling, and professional logging - all with complete type safety and inference.
342 lines (326 loc) • 10.9 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/client/index.ts
var client_exports = {};
__export(client_exports, {
makeServerActionImmutableHook: () => make_server_action_immutable_hook_default,
makeServerActionMutationHook: () => make_server_action_mutation_hook_default
});
module.exports = __toCommonJS(client_exports);
// src/client/helpers/make-server-action-immutable-hook.ts
var import_immutable = __toESM(require("swr/immutable"));
// src/server/helpers/make-server-action.ts
var import_async_xtate = require("async-xtate");
// src/server/helpers/parse-server-error.ts
var import_http_status_codes2 = require("http-status-codes");
// src/server/errors/server-error.ts
var import_http_status_codes = require("http-status-codes");
var ServerError = class extends Error {
constructor(props) {
super(props.message);
this.name = "ServerError";
this.code = props.code ?? "GENERIC_ERROR";
this.status = props.status ?? import_http_status_codes.StatusCodes.INTERNAL_SERVER_ERROR;
this.uiMessage = props.uiMessage;
}
toJSON() {
return {
message: this.message,
code: this.code,
status: this.status,
uiMessage: this.uiMessage
};
}
};
// src/server/helpers/parse-server-error.ts
var import_zod = require("zod");
var defaultOptions = {
defaultMessage: "An unexpected error occurred",
defaultCode: "GENERIC_ERROR" /* GenericError */,
defaultStatus: import_http_status_codes2.StatusCodes.INTERNAL_SERVER_ERROR,
defaultUiMessage: "An unexpected error occurred"
};
// src/server/helpers/logger/log-levels.ts
var LOG_LEVEL_PRIORITY = {
["ERROR" /* ERROR */]: 0,
["WARN" /* WARN */]: 1,
["INFO" /* INFO */]: 2,
["DEBUG" /* DEBUG */]: 3,
["TRACE" /* TRACE */]: 4
};
var LOG_LEVEL_COLORS = {
["ERROR" /* ERROR */]: "\x1B[31m",
// Red
["WARN" /* WARN */]: "\x1B[33m",
// Yellow
["INFO" /* INFO */]: "\x1B[36m",
// Cyan
["DEBUG" /* DEBUG */]: "\x1B[35m",
// Magenta
["TRACE" /* TRACE */]: "\x1B[37m"
// White
};
var RESET_COLOR = "\x1B[0m";
// src/server/helpers/logger/transports/console-transport.ts
var ConsoleTransport = class {
log(formattedMessage, entry) {
switch (entry.level) {
case "ERROR" /* ERROR */:
console.error(formattedMessage);
break;
case "WARN" /* WARN */:
console.warn(formattedMessage);
break;
case "INFO" /* INFO */:
console.info(formattedMessage);
break;
case "DEBUG" /* DEBUG */:
case "TRACE" /* TRACE */:
console.debug(formattedMessage);
break;
default:
console.log(formattedMessage);
}
}
};
var console_transport_default = ConsoleTransport;
// src/server/helpers/logger/formatters/pretty-formatter.ts
var PrettyFormatter = class {
format(entry) {
const timestamp = entry.timestamp.toISOString();
const color = LOG_LEVEL_COLORS[entry.level];
const levelText = `[${entry.level}]`.padEnd(7);
let message = `${color}${timestamp} ${levelText}${RESET_COLOR}`;
if (entry.context) {
message += ` [${entry.context}]`;
}
if (entry.requestId) {
message += ` [${entry.requestId}]`;
}
message += ` ${entry.message}`;
if (entry.metadata && Object.keys(entry.metadata).length > 0) {
message += `
Metadata: ${JSON.stringify(entry.metadata, null, 2)}`;
}
return message;
}
};
var pretty_formatter_default = PrettyFormatter;
// src/server/helpers/logger/logger.ts
var Logger = class _Logger {
constructor(config = {}) {
this.config = {
level: config.level ?? "INFO" /* INFO */,
context: config.context ?? "APP",
formatter: config.formatter ?? new pretty_formatter_default(),
transports: config.transports ?? [new console_transport_default()],
includeTimestamp: config.includeTimestamp ?? true,
includeContext: config.includeContext ?? true,
includeMetadata: config.includeMetadata ?? true
};
}
shouldLog(level) {
return LOG_LEVEL_PRIORITY[level] <= LOG_LEVEL_PRIORITY[this.config.level];
}
async writeLog(level, message, metadata, requestId) {
if (!this.shouldLog(level)) {
return;
}
const entry = {
level,
message,
timestamp: /* @__PURE__ */ new Date(),
...this.config.includeContext && { context: this.config.context },
...this.config.includeMetadata && metadata && { metadata },
...requestId && { requestId }
};
const formattedMessage = this.config.formatter.format(entry);
const logPromises = this.config.transports.map(
(transport) => Promise.resolve(transport.log(formattedMessage, entry))
);
await Promise.all(logPromises);
}
error(message, metadata, requestId) {
return this.writeLog("ERROR" /* ERROR */, message, metadata, requestId);
}
warn(message, metadata, requestId) {
return this.writeLog("WARN" /* WARN */, message, metadata, requestId);
}
info(message, metadata, requestId) {
return this.writeLog("INFO" /* INFO */, message, metadata, requestId);
}
debug(message, metadata, requestId) {
return this.writeLog("DEBUG" /* DEBUG */, message, metadata, requestId);
}
trace(message, metadata, requestId) {
return this.writeLog("TRACE" /* TRACE */, message, metadata, requestId);
}
child(context) {
return new _Logger({
...this.config,
context: `${this.config.context}:${context}`
});
}
setLevel(level) {
this.config.level = level;
}
setFormatter(formatter) {
this.config.formatter = formatter;
}
addTransport(transport) {
this.config.transports.push(transport);
}
};
var logger_default = Logger;
// src/server/helpers/logger/request-logger.ts
var RequestLogger = class {
constructor(logger) {
this.logger = logger.child("REQUEST");
}
extractRequestData(request) {
const url = new URL(request.url);
const userAgent = request.headers.get("user-agent") || void 0;
const ip = request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || void 0;
return {
method: request.method,
url: url.pathname + url.search,
userAgent,
ip,
headers: Object.fromEntries(request.headers.entries())
};
}
generateRequestId() {
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
async logRequest(request) {
const requestId = this.generateRequestId();
const requestData = this.extractRequestData(request);
await this.logger.info(
`Incoming ${requestData.method} ${requestData.url}`,
{ requestData },
requestId
);
return requestId;
}
async logResponse(requestId, statusCode, duration, additionalData) {
const message = `Response ${statusCode} (${duration}ms)`;
const metadata = { statusCode, duration, ...additionalData };
if (statusCode >= 400) {
await this.logger.warn(message, metadata, requestId);
} else {
await this.logger.info(message, metadata, requestId);
}
}
async logError(requestId, error, additionalData) {
await this.logger.error(
`Request error: ${error.message}`,
{
error: error.name,
stack: error.stack,
...additionalData
},
requestId
);
}
};
var request_logger_default = RequestLogger;
// src/server/helpers/server-logger.ts
var defaultLogger = new logger_default({
level: false ? "INFO" /* INFO */ : "DEBUG" /* DEBUG */,
context: "SERVER"
});
var requestLogger = new request_logger_default(defaultLogger);
// src/server/helpers/make-api-controller.ts
var import_http_status_codes3 = require("http-status-codes");
var import_server = require("next/server");
// src/client/helpers/make-server-action-immutable-hook.ts
function makeServerActionImmutableHook(options) {
const { key, action } = options;
const makeKey = (context) => ({
key,
...context ? { context } : {}
});
function useAction(hookOptions) {
const { context, options: swrOptions, skip = false } = hookOptions;
const currentKey = skip ? null : makeKey(context);
return (0, import_immutable.default)(
currentKey,
async (args) => {
const result = await action(args.context);
if (result.status === "error") throw new ServerError(result.error);
if (result.status !== "success")
throw new ServerError({
message: "Unknown error",
code: "UNKNOWN_ERROR"
});
return result.data;
},
swrOptions
);
}
;
return {
useAction,
makeKey
};
}
var make_server_action_immutable_hook_default = makeServerActionImmutableHook;
// src/client/helpers/make-server-action-mutation-hook.ts
var import_mutation = __toESM(require("swr/mutation"));
function makeServerActionMutationHook(options) {
const { key, action } = options;
const makeKey = (context) => ({
key,
...context ? { context } : {}
});
function useAction(hookOptions) {
const { context, options: swrOptions } = hookOptions || {};
const currentKey = makeKey(context);
return (0, import_mutation.default)(
currentKey,
async (_, { arg }) => {
const result = await action(arg);
if (result.status === "error") throw new ServerError(result.error);
if (result.status !== "success")
throw new ServerError({
message: "Unknown error",
code: "UNKNOWN_ERROR"
});
return result.data;
},
swrOptions
);
}
;
return {
useAction,
makeKey
};
}
var make_server_action_mutation_hook_default = makeServerActionMutationHook;